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;
    }
    
  • 相关阅读:
    Web的26项基本概念和技术
    StringComparison枚举
    WebDriver使用指南(完整篇)
    Sublime Text 2 入门及技巧
    每天一个 Linux 命令(16):which whereis locate命令
    每天一个 Linux 命令(13):less 命令
    每天一个 Linux 命令(12):more命令
    每天一个linux命令(11):nl命令
    每天一个linux命令(10):cat 命令
    每天一个命令 ls
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11297785.html
Copyright © 2011-2022 走看看