zoukankan      html  css  js  c++  java
  • Codeforces Round #715 (Div. 2) CDEF

    Codeforces Round #715 (Div. 2) CDEF

    C. The Sports Festival DP

    题目大意:

    给你一个序列 (s) ,你可以重新排列这个序列,成为一个新序列 (a) 。设 (d_i=max(a_1,..,a_i)-min(a_1,...,a_i)) ,求最小的 (d_1+d_2+...+d_n)

    题解:

    倒过来想,就是对于一个已经排好的序列,每次选择删掉最大值还是最小值,因为删掉中间的值是不会有影响的。

    定义 (dp[i][j]) 表示从 (i)(j) 的最小的值。

    然后转移每次只能从 (dp[i+1][j]) 转移过来,或者从 (dp[i][j-1]) 转移过来。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 2e3 + 10;
    typedef long long ll;
    ll dp[maxn][maxn],a[maxn];
    
    int main(){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+1+n);
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==1) dp[j][j] = 0;
                else dp[j][i+j-1] = min(dp[j+1][i+j-1],dp[j][i+j-2]) + a[i+j-1]-a[j];
            }
        }
        printf("%lld
    ",dp[1][n]);
    }
    

    D Binary Literature 思维

    题目大意:

    给你三个长度为 (2n)(01) 串,让你构造一个长度为 (3n)(01) 串,并且这个 (01) 必须包含这三个 (01) 串中至少 2 个作为它的子序列。看样例理解。

    题解:

    因为长度都为 (2n)(01) 串,所以要么 (0) 的数量大于等于 (1) 的数量,要么 (1) 的数量大于 (0) 的数量,三个串分成两类,所以至少存在两个串属于同一类。

    假设:(A) 串和 (B) 串都是 (0) 的数量大于等于 (1) 的数量,那么显而易见就是:

    • (B) 开头的 1 全部放入答案,然后找到第一个是 0 的位置存下来
    • 接下来就把 (A) 顺序放入答案,如果第 (i) 个位置是 0,那么同时 (B) 也可以往后移动一格
    • 如果 (B) 往后移动到的这个位置是 (1) ,那么把 (B) 的这一段连续的 1 全部放入答案。
    • 重复2 3 操作
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 3e5 + 10;
    int s[3][maxn],ans[maxn],n,num[3];
    /*
    011001
    111010
    
    100110
    000101
     */
    void solve(int x,int y,int f) {
        int now = 0, pos = 1;
        while (pos <= 2 * n && s[y][pos] == 1) ans[++now] = 1, pos++;
        for (int j = 1; j <= 2 * n; j++) {
            ans[++now] = s[x][j];
            if (s[x][j] == 0) {
                if (pos <= 2 * n) pos++;
                while (s[y][pos] == 1 && pos <= 2 * n) ans[++now] = 1, pos++;
            }
        }
        while (now <= 3 * n) ans[++now] = 0;
        if (f) {
            for (int i = 1; i <= 3 * n; i++) ans[i] ^= 1;
        }
    }
    bool check(int x,int y){
        if(num[x]>=n&&num[y]>=n) {
            for(int i=1;i<=2*n;i++){
                s[x][i] ^=1;
                s[y][i] ^=1;
            }
            if(num[x]<num[y]) swap(x,y);
            solve(x,y,1);
            return true;
        }
        else if(num[x]<=n&&num[y]<=n){
            if(num[x]>num[y]) swap(x,y);
            solve(x,y,0);
            return true;
        }
        return false;
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n);
            for(int i=0;i<=2;i++){
                num[i] = 0;
                for(int j=1;j<=2*n;j++){
                    scanf("%1d",&s[i][j]);
                    num[i] += s[i][j];
                }
            }
            bool flag = true;
            for(int i=0;i<=2&&flag;i++){
                for(int j=i+1;j<=2&&flag;j++){
                    if(check(i,j)) {
                        flag = false;
                    }
                }
            }
            for(int i=1;i<=3*n;i++) printf("%d",ans[i]);
            printf("
    ");
        }
    }
    

    E Almost Sorted 思维

    题目大意:

    给你一个排列,定义一个近乎顺序的序列, (a_{i+1}geq a_i-1) 。求这个排列的第 (k) 大的近乎顺序排列。

    题解:

    这个题目其实不是很难,首先你要写一下这个排列,观察这个排列的性质:

    (n = 6) 为例:

    1) 1 2 3 4 5 6
    2) 1 2 3 4 6 5
    3) 1 2 3 5 4 6
    4) 1 2 3 6 5 4
    5) 1 2 4 3 5 6
    6) 1 2 4 3 6 5
    7) 1 2 5 4 3 6
    8) 1 2 6 5 4 3
    9) 1 3 2 4 5 6
    10)1 3 2 4 5 6
    11)1 3 2 5 4 6
    12)1 3 2 6 5 4
    13)1 4 3 2 5 6
    14)1 4 3 2 6 5
    15)1 5 4 3 2 6
    16)1 6 5 4 3 2
    ...
    
    • 观察这个序列,容易发现:第一个是1 的有 16个,在第一个是1 的情况下第二个是2的有8个,依此类推,容易发现:如果 (k<=16) ,那么 (ans[1] = 1)(k<=8) ,那么 (ans[2]=2)
    • 对上面的式子总结,可以发现,(k<2^{n - i - 1})(ans[i] = i)
    • 继续观察,如果 (n = 6,k = 6) ,那么:(ans[1] = 1,ans[2] = 2) ,然后不确定 (ans[3]) ,枚举这个位置是多少,如果是 4 ,那么3 一定紧接着4,但是5 和 6 是没有约束的,所以此时的种类就是 (2^{n - 4 - 1}) ,如果之前的一个基数 (res)(此时也就是当 (ans[3] = 3)(res = 4))是小于 k,并且 (res + 2^{n- 4 - 1}leq k) ,那么说明 (ans[3] = 4,ans[4] = 3) ,然后再去判断 (ans[5],ans[6])

    差不多就是上述的思路,写起来注意一下细节即可。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 10;
    typedef long long ll;
    int ans[maxn];
    ll f[110];
    
    int main() {
        f[0] = 1;
        for (int i = 1; i <= 61; i++) f[i] = f[i - 1] * 2;
        int T;
        scanf("%d",&T);
        while(T--){
            ll n, k;
            scanf("%lld%lld", &n, &k);
            int pos = -1;
            for (int i = 1; i < n; i++) {
                if (n - i - 1 >= 61) continue;
                if (f[n - i - 1] < k) {
                    pos = i;
                    break;
                }
            }
            if(n - 1<=60 && f[n-1]<k) {
                printf("-1
    ");
                continue;
            }
            if (pos == -1) {
                for (int i = 1; i <= n; i++) ans[i] = i;
            } else {
                ll res = 0;
                for (int i = 1; i < pos; i++) ans[i] = i;
                while (pos <= n) {
                    for (int i = pos; i <= n; i++) {
                        ll tmp = 1;
                        if (n - i - 1 >= 0) tmp = f[n - i - 1];
                        if (res < k && res + tmp >= k) {
                            int x = pos;
                            for (int j = i; j >= x; j--) ans[pos++] = j;
                            break;
                        } else res += tmp;
                    }
                }
            }
            for (int i = 1; i <= n; i++) {
                printf("%d",ans[i]);
                if(i==n) printf("
    ");
                else printf(" ");
            }
        }
    }
    
    

    F. Complete the MST 思维

    题目大意:

    你有一张完全图,给了你一部分边 ((u,v,w)) ,要求你把剩下的边的权值赋值,使得这个完全图的所有边的权值异或为 (0) ,定义这棵树的丑陋程度为这棵树的最小生成树的所有边的权值之和,求这棵树最小的丑陋程度是多少?

    题解:

    • 很容易发现,最后的边只有一条等于存在的边的异或值,剩下都是 (0)

    • 用一个 (set) 存下 ([1,n]) 所有的点,然后每次取出一个点 (x),然后枚举每一个在 (set) 里面的点 (y),如果与里面的点有边相连,则把这条边存下来,如果没有,则说明说明可以连一条权值为 0 的边,那么直接将这个点从 (set) 里面拿出来,放入这棵树中

    • 最后再判断存下的边,还需要多少条才能构建这棵树

    • 假设上述操作构建了 (x) 棵树,那么接下来还需要 (x - 1) 条边。

    • 所以如果 (n*(n-1)/2 - m > (n - 1) - (x - 1)) ,那么就说明之前使用权值为 0 这条边构建的树是可以。

    • 接下来要让 (x) 棵树变成1棵,已经是不能使用权值为0的了,因为遍历完所有的点之后,无法使用权值权值为 0 的边让他们连接起来,就说明已经是这些点只能用有权值的边相连。

    • 然后求出 (x) 个点的最小生成树即可。

    • 最后如果 (n*(n - 1)/2 - m leq (n - 1) - (x - 1)) ,那么就必须让一条权值为 0 的边变成 (cur) ((cur) 为给定的所有的边的权值的异或和) ,但是可能之后选择的边会更优,所以要进行比对。

    • 怎么比对呢?首先我们要明白比对什么?因为前面权值为0的边应该要有一个替换为 (cur) ,但是可能这条边不替换成 (cur) 而是替换成之后已有的连边可能更优。所以我们是要比对每一条0边是把这个长度换成 (cur) 更优还是等价替换成之后已经给出来的边更优。

    • 那这样怎么写呢?我只需要看是否存在一条权值 (w) 小于 (cur) 的边,使得两个由 (0) 边连接起来的点连通即可,找到最小的 (w) 即可。

    • 因为第 (7) 步是使用的给定的边合并出来的,所以要把这些边排除。

    • 单独使用一个并查集只将第 (7) 步并起来的点合并,然后遍历给定的边,判断是否存在两个点使得两个不在同一棵树的两个点合并。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 2e5 + 10;
    typedef long long ll;
    struct node{
        int u,v;
        long long w;
        node(int u=0,int v=0,long long w=0):u(u),v(v),w(w){}
    }e[maxn];
    bool cmp(node a,node b) {
        return a.w < b.w;
    }
    set<int>st;
    int fa[maxn],ah[maxn];
    queue<int>que;
    map<int,int>mp[maxn];
    void bfs(int x) {
        while (!que.empty()) que.pop();
        que.push(x), st.erase(x);
        while (!que.empty()) {
            int u = que.front();
            que.pop();
            vector<int> rm;
            for (auto now:st) {
                if (!mp[u].count(now)) {
                    rm.push_back(now);
                    fa[now] = x;
                    que.push(now);
                }
            }
            for (auto now:rm) st.erase(now);
        }
    }
    int findx(int x) {
        return x == fa[x] ? x : fa[x] = findx(fa[x]);
    }
    int findah(int x) {
        return x == ah[x] ? x : ah[x] = findah(ah[x]);
    }
    bool Unite(int x,int y) {
        x = findx(x), y = findx(y);
        if (x == y) return false;
        fa[x] = y;
        return true;
    }
    int main() {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            e[i] = node(u, v, w);
            mp[u][v] = 1, mp[v][u] = 1;
        }
        int cnt_tree = 0;
        for (int i = 1; i <= n; i++) st.insert(i), fa[i] = i, ah[i] = i;
        for (int i = 1; i <= n; i++) {
            if (fa[i] == i) {
                ++cnt_tree;
                bfs(i);
            }
        }
        sort(e + 1, e + 1 + m, cmp);
        ll res = 0, cur = 0;
        for (int i = 1; i <= m; i++) cur ^= e[i].w;
        for (int i = 1; i <= m; i++) {
            int u = e[i].u, v = e[i].v, w = e[i].w;
            if (Unite(u, v)) {
                res += w;
                u = findah(u), v = findah(v);
                if (u != v) ah[u] = v;
            }
        }
    //    printf("!!! res = %lld cnt = %d
    ",res,cnt_tree);
        if (1ll * n * (n - 1) / 2 - m > (n - 1) - (cnt_tree - 1)) printf("%lld
    ", res);
        else {
            for (int i = 1; i <= m; i++) {
                int u = e[i].u, v = e[i].v, w = e[i].w;
                int fu = findah(u), fv = findah(v);
                if (fu != fv) cur = min(cur, 1ll * w);
            }
            res += cur;
            printf("%lld
    ", res);
        }
    }
    
    
  • 相关阅读:
    [JavaScript] ObjectiveC参数列表语法转换工具。可转为UML或C++语法,用于绘制UML
    ccpuid:CPUID信息模块 V1.01版,支持GCC(兼容32位或64位的Windows/Linux)
    [C++] 测试硬件popcnt(位1计数)指令与各种软件算法,利用模板实现静态多态优化性能
    [C] zintrin.h: 智能引入intrinsic函数 V1.02版。支持VC2012,增加INTRIN_ALIGN、INTRIN_COMPILER_NAME宏
    [C] 让VC、BCB支持C99的整数类型(stdint.h、inttypes.h)(兼容GCC)
    [C#] TestHttpPost:测试Http的POST方法的小工具
    [C] 跨平台使用TCHAR——让Linux等平台也支持tchar.h,解决跨平台时的格式控制字符问题,多国语言的同时显示(兼容vc/gcc/bcb,支持Windows/Linux/Mac)
    GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表
    [VC] ccpuid:CPUID信息模块。范例:显示所有的CPUID信息
    [C++] cout、wcout无法正常输出中文字符问题的深入调查(1):各种编译器测试
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/14674725.html
Copyright © 2011-2022 走看看