2019牛客暑期多校训练营(第五场)

今天队友不仅又爆零了,还送我几发罚时(下次再这样就要请客谢罪了)
A digits 2

解法:算出n每为数字的和x之后,算出x与n的lcm,输出lcm / x 个n即可
#include
using namespace std;
int gao(int x) {
    if (!x)
        return 0;
    return x % 10 + gao(x / 10);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n;
        cin>>n;
        int x = gao(n);
        int d = __gcd(x, n);
        int lcm = n * x / d;
        //printf("lcm = %d\n", lcm);
        int k = lcm / x;
        for (int i = 1; i <= k; i++)
            printf("%d", n);
        puts("");
    }
}

B generator 1

首先先构造矩阵,然后跑十进制的矩阵快速幂即可,水题
#include
#define ll long long
using namespace std;
const int N = 2;
ll tmp[N][N], mod;
void add(ll &x, ll y) {
    x += y;
    if (x >= mod)
        x -= mod;
}
void multi(ll a[][N],ll b[][N],int n)
{
    memset(tmp,0,sizeof tmp);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        for(int k=0;k<n;k++)
        add(tmp[i][j], a[i][k]*b[k][j] % mod);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            a[i][j]=tmp[i][j];
}
ll res[N][N];
int len;
const int maxn = 1e6 + 10;
char s[maxn];
void Pow(ll a[][N])
{
    memset(res,0,sizeof res);
    for(int i=0;i<N;i++) res[i][i]=1;
    for (int i = len; i; i--)
    {
        int m = s[i] - '0';
        for (int j = 1; j <= m; j++)
            multi(res, a, N);
        ll tmp2[2][2];
        tmp2[0][0] = a[0][0];
        tmp2[0][1] = a[0][1];
        tmp2[1][0] = a[1][0];
        tmp2[1][1] = a[1][1];
        for (int j = 1; j <= 3; j++)
        multi(a, a, N);
        multi(a, tmp2, N);
        multi(a, tmp2, N);
    }
}
void gao() {
    int p = len;
    while (s[p] == '0')
        s[p] = '9', p--;
    s[p]--;
}
int main() {
    int x0, x1, a, b;
    scanf("%d%d%d%d", &x0, &x1, &a, &b);
    scanf("%s%d", s + 1, &mod);
    len = strlen(s + 1);
    gao();

    ll A[2][2] = {a, b, 1, 0};
    Pow(A);
    ll ans = res[0][0] * x1 % mod + res[0][1] * x0 % mod;
    printf("%lld\n", ans % mod);
}

C generator 2
带公式的题解都在这儿

E independent set 1

题意:给一个无向图,设图的权值为该图中最多的独立点(即任意两点无边连接)的数量,求题目给的无向图的所有子图的总权值和
解法:我靠巨水,要是放弃F题就好了,设d[i]为子图点集为 i (二进制下),的最多独立点数量,对于d[i],我们找到 i 的最右边1的位置k(其他的1转移也可以,考虑最右边是因为这样复杂度小),如果我们不加这个点d[i] = max(d[i], d[i ^ (1 << k)]),假设G[k]为与 k 点不连边的点集,如果加k点,d[i] = max(d[i], (i & G[k]) + 1)
#include
#define ll long long
using namespace std;
int G[26];
char d[1 << 26];
int main() {
    int n, m, u, v;
    cin>>n>>m;
    while (m--) {
        cin>>u>>v;
        G[u] |= (1 << v);
        G[v] |= (1 << u);
    }
    int ans = 0, s = (1 << n) - 1;
    for (int i = 0; i < n; i++)
        G[i] = (s ^ (G[i] | (1 << i)));
    for (int i = 1; i < (1 << n); i++) {
        int x;
        for (x = 0; ; x++)
            if (i >> x & 1)
                break;
        d[i] = max(d[i ^ (1 << x)], char(d[i & G[x]] + 1));
        ans += d[i];
    }
    cout<<ans;
}

F maximum clique 1

当时知道就是个二分图求最大独立集方案,但是写不出罚座一下午。首先我们把任意两个只有一个bit不同的数连边(说白了就是二进制下1的个数只相差1),可以证明一下该图肯定是二分图,也就是没有奇环:对于一个起点o,假设它存在于一个奇环中,那么直接与o相连的两个数x,y的1(二进制下)的个数要么相同要么相差2(即偶数),假设环的长度是3显然x,y不应该有边相连,与假设矛盾,延伸到任意奇数长度环,最后两个相连的数1的个数相差都为偶数,与假设矛盾。因此我们直接套二分图最大独立集模板就行
#include
using namespace std;
const int maxn = 5010;
int a[maxn], b[maxn], n, col[maxn], Left[maxn], Right[maxn], vis[maxn];
vector<int> G[maxn], ans, tmp;
void dfs(int u, int color) {
    tmp.push_back(u);
    col[u] = color;
    for (auto v : G[u])
        if (!col[v])
            dfs(v, 3 - color);
}
int dfs2(int u) {
    vis[u] = 1;
    for (auto v : G[u])
        if (!vis[v]) {
            vis[v] = 1;
            if (!Left[v] || dfs2(Left[v])) {
                Left[v] = u;
                Right[u] = v;
                return 1;
            }
        }
    return 0;
}
void gao() {
    int color = 2;
    for (auto u : tmp)
        if (col[u] == color) {
            for (auto v : tmp)
                vis[v] = 0;
            dfs2(u);
        }

    for (auto u : tmp)
        vis[u] = 0;
    for (auto u : tmp)
        if (col[u] == color && !Right[u])
            dfs2(u);
    for (auto u : tmp)
        if ((col[u] == color && vis[u]) || (col[u] == 3 - color && !vis[u]))
            ans.push_back(b[u]);
}
int main() {
    cin>>n;
    for (int i = 1; i <= n; i++)
        cin>>a[i], b[i] = a[i];
    sort(b + 1, b + 1 + n);
    for (int i = 1; i <= n; i++)
        a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 30; j++) {
            int x = b[i] ^ (1 << j);
            int k = lower_bound(b + 1, b + 1 + n, x) - b;
            if (b[k] == x)
                G[i].push_back(k);
        }
    for (int i = 1; i <= n; i++) {
        if (col[i])
            continue;
        tmp.clear();
        dfs(i, 1);
        gao();
    }
    printf("%d\n", ans.size());
    for (int i = 0; i < ans.size(); i++)
        printf("%d%c", ans[i], (i == ans.size() - 1) ? '\n' : ' ');
}

G subsequence 1

解法:首先从 s 串中选的数字长度大于 t 串长度,肯定ok,那么我们枚举第一个数字的位置并且用组合数搞一搞就可以了,接下来我们用dp搞长度与 t 串相等且大于 t 的数字数量即可,设d[i][j]为 s 串前 i 个数字中,选了 j 个数字且大于 t 串的前 j 个数组的方案数,f[i][j]为与 t 串相等的方案数,对于 s 串第 i 个数字,我不选 :d[i][j] = d[i - 1][j],f[i][j] = f[i - 1][j],选:d[i][j] += d[i - 1][j - 1],如果s[i] > t[j],那么d[i][j] += f[i - 1][j - 1],如果s[i] = t[j],那么f[i][j] += f[i - 1][j - 1]
#include
#define ll long long
using namespace std;
const int maxn = 3005, mod = 998244353;
int d[maxn][maxn], c[maxn][maxn], f[maxn][maxn];
char s[maxn], t[maxn];
void add(int &x, int y) {
    x += y;
    if (x >= mod)
        x -= mod;
    if (x < 0)
        x += mod;
}
int main() {
    int T;
    scanf("%d", &T);
    c[0][0] = 1;
    for (int i = 1; i <= 3000; i++) {
        c[i][0] = c[i][i] = 1;
        for (int j = 1; j < i; j++)
            add(c[i][j], c[i - 1][j - 1] + c[i - 1][j]);
    }
    for (int i = 1; i <= 3000; i++)
        for (int j = i; j >= 0; j--)
            add(c[i][j], c[i][j + 1]);
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        scanf("%s%s", s + 1, t + 1);
        int ans = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                d[i][j] = f[i][j] = 0;
        for (int i = 1; i <= n; i++) {
            f[i - 1][0] = 1;
            if (s[i] != '0' && (n - i + 1 > m))
                add(ans, c[n - i][m]);
            for (int j = 1; j <= min(i, m); j++) {
                add(f[i][j], f[i - 1][j]);
                add(d[i][j], d[i - 1][j]);
                add(d[i][j], d[i - 1][j - 1]);
                if (s[i] > t[j])
                    add(d[i][j], f[i - 1][j - 1]);
                else if (s[i] == t[j])
                    add(f[i][j], f[i - 1][j - 1]);
            }
        }
        add(ans, d[n][m]);
        printf("%d\n", ans);
    }
}

H subsequence 2

解法:我们对每个字母,连有向边到它前面一个字母,然后拓扑排序即可,现场我竟然写了线段树优化拓扑图…线段树优化拓扑图:对于每个字母,对它前面所有字母都连有向边,可以线段树区间+1实现连边操作,然后拓扑排序即可
#include
using namespace std;
const int maxn = 10100;
struct node {
#define ls o * 2
#define rs o * 2 + 1
#define mid (l + r) / 2
    int mn[maxn * 4], tag[maxn * 4];
    void pushdown(int o) {
        tag[ls] += tag[o]; tag[rs] += tag[o];
        mn[ls] += tag[o]; mn[rs] += tag[o];
        tag[o] = 0;
    }
    void up(int o, int l, int r, int  ql, int qr, int v) {
        if (ql > qr)
            return;
        if (l >= ql && r <= qr) {
            mn[o] += v;
            tag[o] += v;
            return;
        }
        pushdown(o);
        if (ql <= mid)
            up(ls, l, mid, ql, qr, v);
        if (qr > mid)
            up(rs, mid + 1, r, ql, qr, v);
        mn[o] = min(mn[ls], mn[rs]);
    }
    int qu(int o, int l, int r) {
        if (mn[o] > 0)
            return -1;
        if (l == r)
            return l;
        pushdown(o);
        if (mn[ls] == 0)
            return qu(ls, l, mid);
        return qu(rs, mid + 1, r);
    }

} tree[10];
int p[10];
char s[maxn];
stack<char> ans;
#define pi pair
#define mk make_pair
vector<pi> G[10][maxn][2];
int vis[26];
int main() {
    int n, m, len;
    char str[3];
    scanf("%d%d", &n, &m);
    int ttt = n;
    int T = m * (m - 1) / 2;
    while (T--) {
        scanf("%s%d", str, &len);
        if (len == 0)
            continue;
        memset(vis, 0, sizeof(vis));
        scanf("%s", s + 1);
        for (int i = 1; i <= len; i++) {
            int x = s[i] - 'a';
            ++vis[x];
            G[x][vis[x]][0].push_back(mk(x, vis[x] - 1));
            tree[x].up(1, 1, ttt, 1, vis[x] - 1, 1);
            int c;
            for (int j = 0; j < 2; j++)
                if (str[j] != s[i])
                    c = str[j] - 'a';

            G[x][vis[x]][1].push_back(mk(c, vis[c]));
            tree[c].up(1, 1, n, 1, vis[c], 1);
        }
        int x = str[0] - 'a';
        tree[x].up(1, 1, n, vis[x] + 1, n, 1e3);

        int y = str[1] - 'a';
        tree[y].up(1, 1, n, vis[y] + 1, n, 1e3);
    }
    while (1) {
        int k = -1, c;
        for (int i = 0; i < m; i++) {
            int x = tree[i].qu(1, 1, ttt);
            if (x != -1) {
                k = x;
                c = i;
                break;
            }
        }
        if (k == -1)
            break;
        ans.push(c + 'a');
        for (auto tmp : G[c][k][0])
            tree[c].up(1, 1, ttt, 1, tmp.second, -1);
        for (auto tmp : G[c][k][1])
            tree[tmp.first].up(1, 1, ttt, 1, tmp.second, -1);

        tree[c].up(1, 1, n, k, k, 1e8);
    }
    if (ans.size() != ttt)
        puts("-1");
    else {
        while (!ans.empty())
            printf("%c", ans.top()), ans.pop();
    }
}

I three points 1
队友博客链接

你可能感兴趣的:(牛客多校,动态规划,图论----拓扑排序,数学----简单题,图论----二分图)