zoukankan      html  css  js  c++  java
  • Codeforces Round #558 (Div. 2)题解

    Codeforces Round #558 (Div. 2)题解

    A. Eating Soup

    水题,直接给出代码。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n, m;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        int remain = n - m;
        if(remain <= n / 2) {
            cout << remain;
            return 0;
        }
        cout << max(n - remain, 1);
        return 0;
    }
    

    B2. Cat Party (Hard Edition)

    问题最后只和每个数出现的次数之间的大小有关,有如下几种情况:

    • 假设出现次数最多为(x)次,那么一个数的出现次数为(x)次,其它数的出现次数为(x-1)次。
    • 假设出现次数最多为(x)次,那么有一个数的出现次数为(1),其余数的出现次数都为(x)次。

    这还是很好想的,只有这两种情况与题意相符合。所以我们用一个数组维护出现次数,另一个数组维护出现次数的次数,再维护一下出现最多的次数为多少,最后扫一遍判断就好了。
    代码如下:(写的时候我傻逼离散化了一下,其实可以不用,这样(O(n))就可以解决)

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n;
    int sum[N], a[N], b[N], cnt[N];
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
        sort(b + 1, b + n + 1);
        int D = unique(b + 1, b + n + 1) - b - 1;
        for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + D +1, a[i]) - b;
        int ans = 1;
        int num = 0;
        int mx = 0, num1;
        for(int i = 1; i <= n; i++) {
            if(sum[a[i]] > 0) cnt[sum[a[i]]]--;
            sum[a[i]]++;
            cnt[sum[a[i]]]++;
            if(sum[a[i]] > mx) {
                mx = sum[a[i]];
                num1 = 1;
            } else if(sum[a[i]] == mx) {
                num1++;
            }
            if(num1 == 1 && cnt[mx - 1] * (mx - 1) == i - mx) ans = i;
            if(num1 * mx == i - 1 && cnt[1] == 1) ans = i;
            if(mx == 1) ans = i;
        }
        cout << ans;
    
        return 0;
    }
    

    C2. Power Transmission (Hard Edition)

    当时比赛的时候被这个题关了,有点可惜唉。
    已知两个点(A(x_1,y_1),B(x_2,y_2)),那么经过这两个点的直线方程为:(y=frac{y_2-y_1}{x_2-x_1}x+y_1-x_1*frac{y_2-y_1}{x_2-x_1}),可以化为:((x_2-x_1)*y-(y_2-y_1)*x=y_1*x_2-y_2*x_1)
    之后我们把形式统一一下,暴力搞就行了。
    代码如下:

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    using namespace std;
    typedef long long ll;
    const int N = 2505;
    int n;
    struct node{
        int x, y;
        bool operator < (const node &A)const{
            if(A.x == x) return y < A.y;
            return x < A.x;
        }
    }a[N], b[N];
    map <pair<int, int>, set<int> > mp;
    map <int, int> c1, c2 ;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i].x >> a[i].y;
        }
        ll ans, tot = 0;
        ans = 0;
        int num = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = i + 1; j <= n; j++) {
                int dx = a[j].x - a[i].x ;
                int dy = a[j].y - a[i].y ;
                int g = __gcd(dx, dy) ;
                if(dx == 0 && dy == 0) continue ;
                dx /= g ;
                dy /= g ;
                if(dy < 0 || (dy == 0 && dx < 0)) {
                    dx = -dx ;
                    dy = -dy;
                }
                int c = dy * a[i].x - dx * a[i].y ;
                if(!mp[MP(dy, dx)].count(c)) {
                    ++tot;
                    mp[MP(dy, dx)].insert(c) ;
                    ans += tot - mp[MP(dy, dx)].size() ;
                }
            }
        }
        cout << ans ;
        return 0;
    }
    

    D. Mysterious Code

    比较套路的一个KMP+DP,因为长度很小,所以先暴力处理出(f(i,j))表示当前在第(i)个位置,后面一位为(j(0<j<26))时,能匹配到的最大位置。这里的匹配是自己和自己匹配,类似于KMP算法中的next数组。
    之后直接由(dp(i,j,k))表示第一个串在(i)位置,第二个串在第(j)个位置,第三个串在第(k)个位置,所求的最大值。
    之后直接转移就行了。至于这里的转移为什么是正确的,可以类比于next指针想一下。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1005, M = 55, MAX = 26;
    const int INF = 1e9;
    int nxts[M][MAX], nxtt[M][MAX] ;
    char s[M], t[M], c[N];
    int tests[N] ;
    int dp[N][M][M] ;
    void Get_Nxts(char *s, int Nxt[][MAX]) {
        int L = strlen(s + 1) ;
        for(int i = 0; i <= L; i++) {
            for(int p = 0; p < MAX; p++) {
                char nxt = p + 'a' ;
                for(int j = min(i + 1, L); j >= 0; j--) {
                    bool flag = true ;
                    for(int k = 1; k < j; k++) {
                        if(s[k] != s[i + 1 - j + k]) flag = false ;
                    }
                    if(s[j] != nxt) flag = false;
                    if(flag) {
                        Nxt[i][p] = j;
                        break ;
                    }
                }
            }
        }
    }
    void getmx(int &x, int y) {
        if(y > x) x = y;
    }
    int main() {
        scanf("%s",c + 1);
        scanf("%s%s",s + 1, t + 1);
        Get_Nxts(s, nxts) ;
        Get_Nxts(t, nxtt) ;
        for(int i = 0; i < N; i++)
            for(int j = 0; j < M; j++)
                for(int k = 0; k < M; k++)
                    dp[i][j][k] = -INF;
        dp[0][0][0] = 0;
        int L1 = strlen(c + 1), L2 = strlen(s + 1), L3 = strlen(t + 1);
        for(int i = 0; i < L1; i++) {
            for(int j = 0; j <= L2; j++) {
                for(int k = 0; k <= L3; k++) {
                    if(dp[i][j][k] == -INF) continue ;
                    if(c[i + 1] == '*') {
                        for(int p = 0; p < MAX; p++) {
                            int d = 0;
                            if(nxts[j][p] == L2) d++;
                            if(nxtt[k][p] == L3) d--;
                            getmx(dp[i + 1][nxts[j][p]][nxtt[k][p]], dp[i][j][k] + d) ;
                        }
                    } else {
                        int d = 0;
                        int p = c[i + 1] - 'a' ;
                        if(nxts[j][p] == L2) d++;
                        if(nxtt[k][p] == L3) d--;
                        getmx(dp[i + 1][nxts[j][p]][nxtt[k][p]], dp[i][j][k] + d) ;
                    }
                }
            }
        }
        int ans = -INF;
        for(int i = 0; i <= L2; i++)
            for(int j = 0; j <= L3; j++)
                getmx(ans, dp[L1][i][j]) ;
        cout << ans;
        return 0;
    }
    

    E. Magical Permutation

    这是一个很巧妙的构造题orz。
    先简单说下题意吧:就是会给一个集合(S),其中包含(S_1,S_2,cdots,S_n)(n)个数。现在要求你找出最大的(x),构造出一个(0)(2^x-1)中的所有数的排列。并且相邻两个数的异或值在这个集合(S)中。
    首先我们可以对这个题进行一些分析:(0)左右的数一定在(S)中,假设其中一个数为(x),那么在(x)另一旁的一个数设为(y),因为(x)^(yin S),可知(y)(S)中某些数的异或值。以此类推,就有我们所有构造出来的数都要为(S)中某些数的异或值。
    到了这里想到了些什么?每个二进制位都存在的线性基!
    所以最大的(x)我们就可以直接通过线性基来构造。之后解决的问题就是具体的方案了。
    这里构造方案需要一点Gray code(格雷码)的知识,但我们先看看具体构造方案:

    void gray_code(int x) {
        cnt = 1;
        cout << x << '
    ' ;
        for(int i = 1; i <= x; i++)
            for(int j = cnt; j > 0; j--) ans[++cnt] = ans[j] ^ now[i] ;
        for(int i = 1; i <= cnt; i++) cout << ans[i] << ' ' ;
    }
    

    这里就短短两行代码,却十分精妙。看了我好半天
    这里一开始有1个数,后面有2个,再后面有4个...以此类推。
    每一次都是将前面的数翻转过来异或当前二进制位上面线性基的值。
    下面来证明一下这样做的正确性:
    当数个数比较少的时候,显然满足条件;
    我们假设前面(2^y)个数满足条件,很容易知道第(2^y)个数一定是集合(S)中的某个值,这个很容易知道,因为最后是和0异或的(可以看看之后代码中now数组中存的值)。那么现在来进行构造,可以知道第(2^y)个数和第(2^{y+1})异或的值也一定是在(S)中的某个数。并且后面(2^y)个数两两之间的异或值也一定在集合(S)中,因为这是由前面构造过来的,多异或了一个now,两两异或这个多异或的就会被抵消,就相当于前面的两个异或,由我们的假设就得证了。

    构造方法和证明说完了,那这和格雷码有什么关系呢。

    格雷码的构造方式可以分治来构造,就是将一个二进制串前面加一个0,然后翻转过来在前面加一个1。(详见我上面给出的链接中的镜射排列部分)
    注意到这个翻转和我们构造时的翻转很像,那么格雷码有什么性质呢?相邻两个数二进制只差一位!
    那么我们将x个线性基的值每一个都看作二进制中的1,那么我们通过类似方法构造出来的也是每个相邻的都差一个二进制1,也不就是一个属于(S)集合中的值吗??
    这样,格雷码和我们的构造方式就联系了起来!是不是感觉十分巧妙,也不负这个题目中的"Magic"了。

    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n, cnt;
    int a[N], p[22], now[22], ans[N];
    void ins(int x) {
        int tmp = x;
        for(int i = 20; i >= 0; i--) {
            if((x >> i) & 1) {
                if(!p[i]) {
                    p[i] = x;
                    now[++cnt] = tmp;
                    break ;
                } else x ^= p[i] ;
            }
        }
    }
    void gray_code(int x) {
        cnt = 1;
        cout << x << '
    ' ;
        for(int i = 1; i <= x; i++)
            for(int j = cnt; j > 0; j--) ans[++cnt] = ans[j] ^ now[i] ;
        for(int i = 1; i <= cnt; i++) cout << ans[i] << ' ' ;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 20; i >= 0; i--) {
            memset(p, 0, sizeof(p)); cnt = 0 ;
            for(int j = 1; j <= n; j++) {
                if(a[j] < 1 << i) ins(a[j]) ;
            }
            if(i == cnt) break ;
        }
        gray_code(cnt) ;
        return 0 ;
    }
    
  • 相关阅读:
    区块链|肖臻《区块链技术与应用》公开课之以太坊篇
    区块链|肖臻《区块链技术与应用》公开课之比特币篇
    复合数据类型
    广播变量和累加器
    mysql_基本操作
    8.10 NOI模拟赛 fzhtql SAM 后缀数组 启发式合并 dsu on tree 树状数组 set 线段树合并
    心态
    7.29 NOI 模拟赛 Stars 状压 dp
    8.2 NOI模拟赛 Palindrome dp 组合计数 分类讨论
    7.30 NOI模拟赛 B Easy Sum 分块 NTT
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10877455.html
Copyright © 2011-2022 走看看