zoukankan      html  css  js  c++  java
  • CodeCraft-20 (Div. 2)

    这一场的B和C都很好玩。题目链接:https://codeforces.com/contest/1316

    A - Grade Allocation

    诚心诚意的送分题。

    *B - String Modification

    挺好玩的一个题目,找规律找了半天。

    题意:给一个长度不超过5000的字符串s,要求选择一个k(1<=k<=n),使得字符串s经过一个变换后的字典序最小,假如有多个这样的k,选择最小的一个。这个变换是:对于所有的i∈[1,n-k+1],依次翻转区间[i,i+k-1],例如:

    s="123456" k=2
    "[21]3456"
    "2[31]456"
    "23[41]56"
    "234[51]6"
    "2345[61]"
    
    s="123456" k=3
    "[321]456"
    "3[412]56"
    "34[521]6"
    "345[612]"
    

    看到这里,就感觉像是一个轮换,但是仔细观察会发现最后一段是有可能反过来的,比如:

    s="12345" k=3
    "[321]45"
    "3[412]5"
    "34[521]"
    

    果断猜测是和奇偶性有关。强行构造出所有的变换,然后sort一遍就完事了。k=x,开头就是s[x],这个是显然的,而且后面的字符一个一个一直跟着直到s[x]~s[n],这个也是显然的,前面的s[1]~s[x-1]的“连接状态”不会被改变,这个也是显然的。关键是前面的s[1]~s[x-1]一共经过了n-k+1次翻转,所以有可能是反序的。

    *C - Primitive Primes

    题意:给一个n个项的多项式,系数都是整数,且未知数项为[0,n-1]次,保证所有的n个系数的gcd为1。再同理给出另一个m个项的多项式。再给一个质数p,求他们两个的多项式乘积的系数中,第几次项的系数不是p(p是质数)的倍数,显然这是有解的,假如多解输出其中任意一解。

    题解:n和m太大,跑不动FFT,但是好像有人还真能卡过去(1497ms)。当时比赛的时候在想为什么题目说“显然这是有解的”,而且也不懂为什么要求p是质数,以及为什么要保证系数的gcd的1。画出多项式乘积的系数的计算方法 (C_k=sumlimits_{i=0}^{k}A_iB_{k-i}) 后,可以看出相邻的两项的系数之间貌似不存在什么转移关系。

    一次偶然的直觉,想到会不会是两个多项式分别的第一个非p的倍数,假设其是x,y,则可以得到:

    p p x ...
    p p p y ...
    

    也就是

    p p x a b c ...
    p p p y d e ...
    
    ep+dp+xy+ap+bp+cp
    

    不管a,b,c,d,e是不是p的倍数,交叉组合之后都被乘了一个p,而xy也不可能是p的倍数,因为p是一个质数。所以上面这个式子模p不为0。

    那么他们的系数的gcd为1,意思就是保证了至少存在一个非p的倍数,否则他们都是p的倍数,gcd至少就是p。也就是说他们本身gcd是不是1倒不影响,只要能找到一个不是p的倍数的数就行了。

    D - Nash Matrix

    题意:给一个n,然后一个n*n(n<=1e3)的矩阵,每个格子的信息表示从其出发最终会停留在哪个点,-1表示永远循环。要求构造一个相同大小的矩阵其中每个格子都唯一标有'U','D','L','R'其中之一或者'X',X表示停留在此处。要求任何时候都不能走出棋盘。

    题解:被样例的构造误导到了奇怪的地方,以为“无限走”就要不断经过起始格子,实际上可以走到某个“核心”处然后在核心处绕圈圈。所以就找出所有的'X'格子,然后从他们开始bfs就把一整块都标记完了,然后找任意一个-1格子,假如它是孤立的-1格子则无解,否则两个-1格子就可以组成一个“核心”,再从这个两个格子的“核心”开始多源bfs就把一整块也标记完了。这时假如还有没被标记的格子,说明它和它的终点格子(一定有终点格子)不连通。

    假如-1是要求经过起始格子并无限循环,除了要求一个连通块大小为偶数之外,还要要求“黑格”和“白格”的数量相等(并不是所有的偶数大小的连通块都可以形成回路),然后应该就是从度数最小的“凸起”格子开始贪心,写一个巨复杂的优先队列,注意删除一个格子的时候是删除一对格子,要把附近的总共6个格子的度数全部减1。

    int n;
    int x[1005][1005];
    int y[1005][1005];
    
    int vis[1005][1005];
    int deg[1005][1005];
    char ans[1005][1005];
    
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {-1, 1, 0, 0};
    char dans[4] = {'R', 'L', 'U', 'D'};
    char adans[4] = {'L', 'R', 'D', 'U'};
    
    queue<pii> Q;
    void bfs1(int ux, int uy) {
        vis[ux][uy] = 1;
        ans[ux][uy] = 'X';
        Q.push({ux, uy});
        while(!Q.empty()) {
            int vx = Q.front().first;
            int vy = Q.front().second;
            Q.pop();
            for(int i = 0; i < 4; ++i) {
                int tx = vx + dx[i];
                int ty = vy + dy[i];
                if(1 <= tx && tx <= n && 1 <= ty && ty <= n) {
                    if(x[tx][ty] == ux && y[tx][ty] == uy && vis[tx][ty] == 0) {
                        vis[tx][ty] = 1;
                        ans[tx][ty] = dans[i];
                        Q.push({tx, ty});
                    }
                }
            }
        }
    }
    
    void bfs2(int ux, int uy) {
        vis[ux][uy] = 1;
        Q.push({ux, uy});
        for(int i = 0; i < 4; ++i) {
            int tx = ux + dx[i];
            int ty = uy + dy[i];
            if(1 <= tx && tx <= n && 1 <= ty && ty <= n) {
                if(x[tx][ty] == -1 && y[tx][ty] == -1 && vis[tx][ty] == 0) {
                    vis[tx][ty] = 1;
                    ans[ux][uy] = adans[i];
                    ans[tx][ty] = dans[i];
                    Q.push({tx, ty});
                    break;
                }
            }
        }
        if(Q.size() == 1) {
            puts("INVALID");
            exit(0);
        }
        while(!Q.empty()) {
            int vx = Q.front().first;
            int vy = Q.front().second;
            Q.pop();
            for(int i = 0; i < 4; ++i) {
                int tx = vx + dx[i];
                int ty = vy + dy[i];
                if(1 <= tx && tx <= n && 1 <= ty && ty <= n) {
                    if(x[tx][ty] == -1 && y[tx][ty] == -1 && vis[tx][ty] == 0) {
                        vis[tx][ty] = 1;
                        ans[tx][ty] = dans[i];
                        Q.push({tx, ty});
                    }
                }
            }
        }
    }
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) {
                scanf("%d%d", &x[i][j], &y[i][j]);
                ans[i][j] = '.';
            }
        }
    
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) {
                if(vis[i][j] == 0 && x[i][j] == i && y[i][j] == j)
                    bfs1(i, j);
            }
        }
    
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) {
                if(vis[i][j] == 0 && x[i][j] == -1 && y[i][j] == -1)
                    bfs2(i, j);
            }
        }
    
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) {
                if(vis[i][j] == 0) {
                    puts("INVALID");
                    exit(0);
                }
            }
        }
    
        puts("VALID");
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j)
                putchar(ans[i][j]);
            putchar('
    ');
        }
    }
    

    E - Team Building

    题意:从n(<=1e5)个人里面,选出恰好p(<=7)个球员和恰好k(p+k<=n)个观众。p个球员对应p种不同的位置,给出每个人作为每种位置的收益,以及作为观众的收益。求最大的收益。

    题解:看见这个p的数据量,感觉像是状压,但是k范围很大,定状态为dp[i][j]表示前i个人位置的占用状态为j的时候,忽略已经选了观众的人数,导致不知道一共选了多少个观众。但是仔细想想这个问题其实很好解决,按作为观众的收益从大到小排序,然后dp到前p+k个人的选法。后面的人就只能成为球员,不能成为观众。

    细节:其实不是简单区分i和p+k等变量的关系,而是要数一数当前占用的观众数量,也就是i个人减去状态j中1的数量,就是观众的数量,假如观众还没满则可以转移观众,否则一定不优。最后会发现过不了样例3,显然是初始化没有标记非法状态的原因。

    int n, p, k;
    struct Node {
        int a, s[7];
        bool operator<(const Node &nd)const {
            return a > nd.a;
        }
    } nd[100005];
    ll dp[2][1 << 7];
    
    int count1(int x) {
        return __builtin_popcount(x);
    }
    
    void test_case() {
        scanf("%d%d%d", &n, &p, &k);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &nd[i].a);
        for(int i = 1; i <= n; ++i) {
            for(int b = 0; b < p; ++b)
                scanf("%d", &nd[i].s[b]);
        }
        sort(nd + 1, nd + 1 + n);
    
        memset(dp, -LINF, sizeof(dp));
        dp[0][0] = 0;
    
        for(int i = 1; i <= n; ++i) {
            int t = i & 1;
            for(int j = 0; j < (1 << p); ++j) {
                dp[t][j] = dp[1 - t][j];
                if(i - count1(j) <= k)
                    dp[t][j] += nd[i].a;
            }
            for(int j = 0; j < (1 << p); ++j) {
                for(int b = 0; b < p; ++b) {
                    if(j & (1 << b))
                        continue;
                    dp[t][j | (1 << b)] = max(dp[t][j | (1 << b)], dp[1 - t][j] + nd[i].s[b]);
                }
            }
    
            /*cout << "nd[" << i << "]=" << nd[i].a;
            for(int b = 0; b < p; ++b)
                cout << "," << nd[i].s[b];
            cout << endl;
            for(int j = 0; j < (1 << p); ++j)
                cout << "dp[" << i << "][" << bitset<7>(j) << "]=" << dp[t][j] << endl;
            cout << endl;*/
        }
        printf("%lld
    ", dp[n & 1][(1 << p) - 1]);
    }
    
  • 相关阅读:
    【模板】对拍程序
    【洛谷比赛】Agent1
    【NOIP2017】宝藏
    【NOIP2017】逛公园
    【NOIP2016】换教室
    【NOIP模拟】挖宝藏
    【NOIP模拟】健美猫
    【NOIP2014】飞扬的小鸟
    【NOIP2015】子串
    【CQOI2007】余数求和
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12418168.html
Copyright © 2011-2022 走看看