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}+b\mod p\),求其通项为:\(x_n=a^nx_0+\frac{b(1-a^n)}{1-a}\),因为题目要求\(x_i=v\mod 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=\lceil\sqrt(p)\rceil\)的形式,然后把\(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 << '\n';
                else if(b == v) cout << 1 << '\n';
                else cout << -1 << '\n';
            }
            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 << '\n';
                else cout << ans << '\n';
            }
            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 << '\n';
                else cout << -1 << '\n';
                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 << '\n';
            else cout << ans << '\n';
        }
    }
    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\n", 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], " \n"[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 << '\n';
    }
    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;
}

猜你喜欢

转载自www.cnblogs.com/heyuhhh/p/11297785.html