zoukankan      html  css  js  c++  java
  • 2019牛客多校第五场题解

    2019牛客多校第五场题解

    题目链接

    A.digits 2

    输出(n)(n)即可。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        int T;
        cin >> T;
        while(T--) {
            int n; cin >> n;
        }
        return 0;
    }
    
    

    B.generator 1

    十进制快速幂,(a^n=(a^2)^{frac{n}{2}})改造为(a^n=(a^{10})^(frac{n}{10}))即可,余数就单独乘一下。
    详见代码:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 3, MAX = 1e6 + 15;
    ll a, b, x0, x1, MOD;
    char s[MAX];
    struct matrix{
        int A[N][N];
        int n,m;
        matrix(){memset(A,0,sizeof(A));}
    };
    int add(ll x, ll y) {
        x += y;
        if(x >= MOD) x -= MOD;
        return x;
    }
    int mul(ll x, ll y) {
        return (x *= y) >= MOD ? x % MOD : x;
    }
    matrix operator * (const matrix &a,const matrix &b){
        matrix ans;
        ans.n=a.n;ans.m=b.m;
        for(int i=1;i<=ans.n;i++)
            for(int j=1;j<=ans.m;j++)
                for(int k=1;k<=b.n;k++)
                    ans.A[i][j] = add(ans.A[i][j], mul(a.A[i][k], b.A[k][j])) ;
        return ans ;
    }
    matrix operator + (const matrix &a,const matrix &b){
        matrix ans;
        ans.n=a.n;ans.m=a.m;
        for(int i=1;i<=ans.n;i++){
            for(int j=1;j<=ans.m;j++){
                ans.A[i][j]=(a.A[i][j]+b.A[i][j])%MOD;
            }
        }
        return ans ;
    }
    matrix qp_Mat(matrix a,ll b){
        matrix ans;
        ans.n=ans.m=a.n;
        for(int i=1;i<=ans.n;i++) ans.A[i][i]=1;
        while(b){
            if(b&1) ans=ans*a;
            a=a*a;
            b>>=1;
        }
        return ans ;
    }
    int main() {
        scanf("%lld%lld%lld%lld", &x0, &x1, &a, &b);
        scanf("%s", s);
        scanf("%lld", &MOD);
        matrix trans;
        trans.n = trans.m = 2;
        trans.A[2][1] = 1; trans.A[1][2] = b; trans.A[2][2] = a;
        matrix ans; ans.n = ans.m = 2;
        ans.A[1][1] = ans.A[2][2] = 1;
        int last = strlen(s) - 1;
        while(last >= 0) {
            if(s[last] != '0') {
                int now = s[last] - '0';
                ans = ans * qp_Mat(trans, now);
            }
            trans = qp_Mat(trans, 10);
            last--;
        }
        matrix A; A.n = 1, A.m = 2;
        A.A[1][1] = x0, A.A[1][2] = x1;
        A = A * ans;
        cout << A.A[1][1] << endl;
        return 0;
    }
    
    

    C.generator 2

    BSGS算法,对于题目给出的(x_i=ax_{i-1}+bmod p),求其通项为:(x_n=a^nx_0+frac{b(1-a^n)}{1-a}),因为题目要求(x_i=vmod p),我们将所有与(a_n)无关的放在等式右边,得到:(a^n=frac{v+frac{b}{a-1}}{x_0+frac{b}{a-1}}mod p)
    根据BSGS,我们会扔一个(a^j)到右边去,每次预处理右边部分,放在哈希表里面,对于左边直接枚举进行查找。在这个题中因为询问次数较多,直接这样很容易T。
    所以可以考虑将预处理提出去,那么我们将(n)写为(i*t+j,t=lceilsqrt(p) ceil)的形式,然后把(a^{it})的逆元乘在右边,在询问前预处理左边的,这样就会快很多。
    这里有个迷的地方就是我将(n)处理为(i*t-j)的形式时一直卡在96...(i*t+j)就过了。。。不知道什么情况。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    struct B{
        const int mod = 524287; // (1 << 19) - 1;
        int tot;
        int h[524288], next[524288], L[524288], v[524288];
        int Find(ll x) {
            int k = h[x & mod];
            while(k != 0) {
                if(L[k] == x) return v[k];
                else k = next[k];
            }
            return -1;
        }
        void Add(int e, int i) {
            tot++;
            next[tot] = h[e & mod];
            L[tot] = e; v[tot] = i;
            h[e & mod] = tot;
        }
        void init(int a, int n) {
            memset(h, 0, sizeof(h)); memset(L, 0, sizeof(L));tot = 0;
            memset(next, 0, sizeof(next)); memset(v, 0, sizeof(v));
            ll t, e = 1;
            t = (int)sqrt(n) + 1;
            for(int i = 0; i < t; i++) {
                if(Find(e) == -1) Add(e, i);
                e = e * a % n;
            }
        }
        ll BSGS(int a, int b, int n, ll v, ll t) { // a ^ x = b (mod n)
            for(int i = 0; i < t; i++) {
                if(Find(b) != -1) return i * t + Find(b);
                b = b * v % n;
            }
            return -1;
        }
    }S;
    int p;
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return ans;
    }
    ll n;
    int x0, a, b, T, q;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> x0 >> a >> b >> p;
            if(a == 0) {
                cin >> q;
                while(q--) {
                    int v; cin >> v;
                    if(x0 == v) cout << 0 << '
    ';
                    else if(b == v) cout << 1 << '
    ';
                    else cout << -1 << '
    ';
                }
                continue;
            }
            if(a == 1) {
                cin >> q; ll tmp = qp(b, p - 2);
                while(q--) {
                    int v; cin >> v;
                    int ans = 1ll * (v - x0 + p) % p * tmp % p;
                    if(ans >= n) cout << -1 << '
    ';
                    else cout << ans << '
    ';
                }
                continue;
            }
            int c = 1ll * b * qp(a - 1, p - 2) % p;
            int t = (int)sqrt(p) + 1;
            S.init(a, p);
            cin >> q;
            while(q--) {
                int v; cin >> v;
                ll x = v + c, y = x0 + c;
                if(y % p == 0) {
                    if(x % p == 0) cout << 0 << '
    ';
                    else cout << -1 << '
    ';
                    continue ;
                }
                ll z = x * qp(y, p - 2) % p;
                int k = qp(qp(a, t), p - 2);
                ll ans = S.BSGS(a, z, p, k, t);
                if(ans == -1 || ans >= n) cout << -1 << '
    ';
                else cout << ans << '
    ';
            }
        }
        return 0;
    }
    

    E.independent set 1

    状压(dp)+背包,因为题目求的是子集。注意空间限制,所以(dp)数组用char类型。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 26;
    char dp[1 << N];
    int e[N];
    int n, m;
    int Max(char x, char y) {
        return x > y ? x : y;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        for(int i = 1; i <= m; i++) {
            int u, v; cin >> u >> v;
            e[u] |= (1 << v);
            e[v] |= (1 << u);
        }
        for(int i = 0; i < n; i++) {
            e[i] ^= (1 << i);
            e[i] = ~e[i];
        }
        int ans = 0;
        for(int i = 1; i < (1 << n); i++) {
            int lb = __builtin_ffs(i) - 1;
            dp[i] = Max(dp[i ^ (1 << lb)], dp[i & e[lb]] + 1);
            ans += dp[i];
        }
        cout << ans;
        return 0;
    }
    
    

    maximum clique 1

    题目要求最大团,将问题转换一下,考虑求最大独立集。
    最大团是任意两点之间(bit)相差超过1,那么其补图就是任意两点之间(bit)之差等于1了(任意两个数都不相等)。对其补图建出来很容易发现其为二分图。假设左边全是(bit)个数为奇数的点,那么右边就是(bit)个数为偶数的点,然后跑个网络流就行了。
    输出方案的时候注意,这里利用的是增光时候的最后一次bfs,然后手玩一下bfs过程,就知道为啥这样输出了,本质和从二分图左边未标记点dfs的方法是一样的:都是从左边未标记点开始,然后访问右边标记点,bfs再利用反边不断标记左边的点,就一直重复。。。所以最后左边未标记的点和右边标记的点就是最小点覆盖的答案,最大独立集把这些点去掉就行。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 5005;
    int n;
    int a[N];
    #define INF 0x3f3f3f3f
    template <class T>
    struct Dinic{
        struct Edge{
            int v, next;
            T flow;
            Edge(){}
            Edge(int v, int next, T flow) : v(v), next(next), flow(flow) {}
        }e[5 * 1000000];
        int head[N], cur[N], tot;
        int dep[N];
        void init() {
            memset(head, -1, sizeof(head)); tot = 0;
        }
        void adde(int u, int v, T w, T rw = 0) {
            e[tot] = Edge(v, head[u], w);
            head[u] = tot++;
            e[tot] = Edge(u, head[v], rw);
            head[v] = tot++;
        }
        bool BFS(int _S, int _T) {
            memset(dep, -1, sizeof(dep));
            queue <int> q; q.push(_S); dep[_S] = 0;
            while(!q.empty()) {
                int u = q.front(); q.pop();
                for(int i = head[u]; ~i; i = e[i].next) {
                    int v = e[i].v;
                    if(dep[v] == -1 && e[i].flow > 0) {
                        dep[v] = dep[u] + 1;
                        q.push(v);
                    }
                }
            }
            return dep[_T] != -1;
        }
        T dfs(int _S, int _T, T a) {
            T flow = 0, f;
            if(_S == _T || a == 0) return a;
            for(int i = head[_S]; ~i; i = e[i].next) {
                int v = e[i].v;
                if(dep[v] != dep[_S] + 1) continue;
                f = dfs(v, _T, min(a, e[i].flow));
                if(f) {
                    e[i].flow -= f;
                    e[i ^ 1].flow += f;
                    flow += f;
                    a -= f;
                    if(a == 0) break;
                }
            }
            if(!flow) dep[_S] = -1;
            return flow;
        }
        T dinic(int _S, int _T) {
            T max_flow = 0;
            while(BFS(_S, _T)) {
                max_flow += dfs(_S, _T, INF);
            }
    
            return max_flow;
        }
    };
    bool is_source[N];
    Dinic <int> D;
    bool ok(int x) {
        return x && ((x & (x - 1)) == 0);
    }
    int main() {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            if(__builtin_popcount(a[i]) & 1) is_source[i] = 1;
        }
        D.init();
        for(int i = 1; i <= n; i++) {
            if(is_source[i]) D.adde(0, i, 1);
            else D.adde(i, n + 1, 1);
        }
        for(int i = 1; i <= n; i++) {
            for(int j = i + 1; j <= n; j++) {
                if(ok(a[i] ^ a[j])) {
                    if(is_source[i]) D.adde(i, j, 1);
                    else D.adde(j, i, 1);
                }
            }
        }
        int ans = n - D.dinic(0, n + 1);
        printf("%d
    ", ans);
        vector <int> res;
        for(int i = 1; i <= n; i++) {
            if(is_source[i]) {
                if(D.dep[i] != -1) res.push_back(a[i]);
            } else {
                if(D.dep[i] == -1) res.push_back(a[i]);
            }
        }
        int SZ = res.size();
        for(int i = 0; i < SZ; i++) {
            printf("%d%c", res[i], " 
    "[i == SZ - 1]);
        }
        return 0;
    }
    

    G.subsequence 1

    一个数要大于另外一个数,要么其位数大于它,要么长度相等时某一位大于它。那么根据这个来搞就行。
    位数大于的情况比较好处理,就是一个组合数的问题:枚举起点然后在后面选若干个(因为不能有前导零)。
    等于的情况先(dp)匹配出相等的方案数,设(dp(i,j))表示第一个串到了(i)位,选出来了(j)个能够和第二个串前(j)位相等的方案数。然后根据下一位之间的大小关系进行转移和计算。

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    const int MAXN = 3e3 + 5, N = 3e3, MAXM = 3e5 + 5, INF = 0x3f3f3f3f, MOD = 998244353;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    using namespace std;
    const int oo = (1e9) - (1e6);
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define pb push_back
    #define RR register
    #define random(a,b) ((a)+rand()%((b)-(a)+1))
    #define all(v) (v.begin(),v.end())
    #define lc(x) c[x][0]
    #define rc(x) c[x][1]
    #define R register int
    typedef long double db;
    typedef unsigned int uint;
    #define G c=getchar()
     
    int t, n, m, dp[MAXN][MAXN];
    char s1[MAXN], s2[MAXN];
    ll fact[MAXN], ifact[MAXN], sum[MAXN][MAXN];
    inline void add(int &x, int y) {
        x += y;
        if (x >= MOD)x -= MOD;
    }
    inline ll C(int n, int m) {
        if (m > n)return 0;
        return fact[n] * ifact[n - m] % MOD*ifact[m] % MOD;
    }
    ll qpow(ll a, ll b) {
        ll ans = 1;
        for (; b; b >>= 1, a = a * a%MOD)if (b & 1)ans = ans * a%MOD;
        return ans;
    }
    void init() {
        fact[0] = fact[1] = 1;
        for (int i = 2; i <= N; i++)fact[i] = fact[i - 1] * i%MOD;
        ifact[N] = qpow(fact[N], MOD - 2);
        for (int i = N - 1; i >= 0; i--)ifact[i] = ifact[i + 1] * (i + 1) % MOD;
        for (int i = 1; i <= N; i++) {
            sum[i][0] = 1;
            for (int j = 1; j <= i; j++) {
                sum[i][j] = (sum[i][j - 1] + C(i, j)) % MOD;
                assert(sum[i][j] >= 0);
            }
        }
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        init();
        cin >> t;
        while (t--) {
            cin >> n >> m;
            cin >> (s1 + 1) >> (s2 + 1);
            for (int i = 0; i <= n; i++)
                for (int j = 0; j <= n; j++)dp[i][j] = 0;
            dp[0][0] = 1;
            for (int i = 1; i <= n; i++) {
                dp[i][0] = 1;
                for (int j = 1; j <= min(i, m); j++) {
                    dp[i][j] = dp[i - 1][j];
                    if (s2[j] != s1[i])continue;
                    add(dp[i][j], dp[i - 1][j - 1]);
                }
            }
            int ans = 0;
            for (int i = 1; i <= n; i++) {
                if (s1[i] != '0' && n - i >= m) {
                    add(ans, (sum[n - i][n - i] - sum[n - i][m - 1] + MOD) % MOD);
                }
                for (int j = 1; j <= min(i, m); j++) {
                    if (s1[i] <= s2[j])continue;
                    add(ans, dp[i - 1][j - 1] * C(n - i, m - j) % MOD);
                }
            }
            cout << ans << '
    ';
        }
        return 0;
    }
    

    H.subsequence 2

    因为知道两两之间的大小关系,最后要确定一个大小关系,所以可以想到拓扑序来搞。
    主要就是注意一下代码的细节就是了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e4 + 5, M = 1e6 + 5;
    int n, m, cnt;
    vector <int> g[26];
    string str;
    struct Edge {
        int v, next;
    }e[M << 1];
    int head[N], tot;
    char mp[N], ans[N];
    void adde(int u, int v) {
        e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
    }
    int in[N], num[26];
    int main() {
        scanf("%d%d", &n, &m);
        memset(num, -1, sizeof(num));
        memset(head, -1, sizeof(head));
        int flag = 0;
        for (int i = 1; i <= m * (m - 1) / 2; i++) {
            char s[10]; scanf("%s", s);
            int len; scanf("%d", &len); getchar();
            getline(cin, str);
            if (cnt > n) {    //之前没这个一直RE
                flag = 1;
                continue;
            }
            int cnt1 = 0, cnt2 = 0;
            for (int j = 0; j < len; j++) {
                if (str[j] == s[0]) cnt1++;
                else cnt2++;
            }
            int t1 = s[0] - 'a', t2 = s[1] - 'a';
            if (num[t1] == -1) {
                num[t1] = cnt1;
                while (cnt1--) g[t1].push_back(++cnt), mp[cnt] = s[0];
            }
            else {
                if (num[t1] != cnt1) flag = 1;
            }
            if (num[t2] == -1) {
                num[t2] = cnt2;
                while (cnt2--) g[t2].push_back(++cnt), mp[cnt] = s[1];
            }
            else {
                if (num[t2] != cnt2) flag = 1;
            }
            if (flag) continue;
            int p1 = 0, p2 = 0;
            for (int j = 0; j < len - 1; j++) {
                if (str[j] == s[0]) {
                    if (str[j + 1] == s[0]) {
                        adde(g[t1][p1], g[t1][p1 + 1]);
                        in[g[t1][p1 + 1]]++;
                    }
                    else {
                        adde(g[t1][p1], g[t2][p2]);
                        in[g[t2][p2]]++;
                    }
                    p1++;
                }
                else {
                    if (str[j + 1] == s[0]) {
                        adde(g[t2][p2], g[t1][p1]);
                        in[g[t1][p1]]++;
                    }
                    else {
                        adde(g[t2][p2], g[t2][p2 + 1]);
                        in[g[t2][p2 + 1]]++;
                    }
                    p2++;
                }
            }
        }
        if (cnt != n || flag) {
            cout << -1;
            return 0;
        }
        int tmp = 0;
        queue <int > q;
        for (int i = 1; i <= cnt; i++) if (!in[i]) q.push(i);
        while (!q.empty()) {
            if ((int)q.size() > 1) { //严格拓扑序,那么队列中只有一个
                cout << -1;
                return 0;
            }
            int u = q.front(); q.pop();
            ans[++tmp] = mp[u];
            int k = 0;
            for (int i = head[u]; i != -1; i = e[i].next) {
                int v = e[i].v;
                if (--in[v] == 0) q.push(v), k++;
            }
        }
        if (tmp != n) {
            cout << -1;
            return 0;
        }
        for (int i = 1; i <= tmp; i++) cout << ans[i];
        return 0;
    }
    
  • 相关阅读:
    Kubernetes 集成研发笔记
    Rust 1.44.0 发布
    Rust 1.43.0 发布
    PAT 甲级 1108 Finding Average (20分)
    PAT 甲级 1107 Social Clusters (30分)(并查集)
    PAT 甲级 1106 Lowest Price in Supply Chain (25分) (bfs)
    PAT 甲级 1105 Spiral Matrix (25分)(螺旋矩阵,简单模拟)
    PAT 甲级 1104 Sum of Number Segments (20分)(有坑,int *int 可能会溢出)
    java 多线程 26 : 线程池
    OpenCV_Python —— (4)形态学操作
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11297785.html
Copyright © 2011-2022 走看看