zoukankan      html  css  js  c++  java
  • Codeforces Round #633 (Div. 1)

    A. Powered Addition (CF 1338 A)

    题目大意

    给定一个(n)个数字的数组(a),对于每一个正整数(x),你可以选择若干个数字(i_1,i_2,i_3,...,i_k),使得(a_{i_j}=a_{i_j}+2^{x-1}, 1 leq j leq k)。当然你也可以不进行选择。求最小的数字(T),使得当(x)取过了(1)~(T)进行操作后,数组(a)是一个非递减数组。

    解题思路

    从左到右,如果(a_i>a_{i+1}),我们就把(a_{i+1})变成(a_i),并记录最大的(a_i-a_{i+1}),最大值在二进制下的位数即是答案。

    操作就很简单,差值在二进制下第(i)位是1就在(x=i)的时候选择那个数就好了。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ++ii) {
            int n;
            read(n);
            int ans=0;
            int qwq=-1e9-7;;
            for(int u,i=1;i<=n;++i){
                read(u);
                if (qwq<=u) qwq=u;
                else{
                    ans=max(ans,qwq-u);
                }
            }
            int cnt=0;
            while(ans){
                ans>>=1;
                ++cnt;
            }
            write(cnt,'
    ');
        }
        return 0;
    }
    


    B. Edge Weight Assignment (CF 1338 B)

    题目大意

    给定一棵无权树,要求对边赋一个正权值,使得任意两个叶子节点之间的路径权值异或和为(0)。求所赋的不同权值数的最小值和最大值。

    解题思路

    如果任意两个叶子节点之间的路径长度是偶数,那么我们对全部边赋(1)即可,最小为(1)

    如果存在奇数长度的,注意到(1igoplus 2igoplus 3=0)。我们把不与叶子节点相连的边赋为(1),是奇数长度的那对叶子节点(a,b)相连的边,一个赋(2),一个赋(3),那其他叶子节点对于这两个叶子节点,如果是偶数路径,则与其叶子节点相连的边的权值一样,奇数路径则相反((2)(3)(3)(2))。

    对于不是(a,b)的一对点(c,d),它们的边权异或和((c,d))一定为(0),因为边权异或和可以拆成((c,d)=(c,a)igoplus(a,d)),因为(c,d)(lca)(a)的边权异或了两次抵消掉了。而在前面的构造里我们知道((c,a)=0,(a,d)=0),所以((c,d)=0)。也即最小为(3)

    判断奇数长度的,从一个叶子节点搜(初始深度为(1))发现有深度是偶数深度的叶子节点即可得知有奇数长度。

    至于最大的,我们可以设想,除了叶子节点相连的边,其他的边都填不同的数,然后选择一个叶子节点(a),其边也填一个不同的数,然后对于其他叶子节点(b),其边填(a,fa[b])的值(fa[b]表示与叶子节点相连的边的另外一个节点)。对于不是叶子节点(a)的点(b,c),其((b,c))也一定为(0),证明也如同上面的方法,将((b,c)=(b,a)igoplus(a,c))

    由于可以填的数无穷大,我们可以证明 (猜想) 这一定可以做到的。

    所以,只有那些一个节点连了多个叶子节点的那些边的权值一定相等外,其他的都可以不一样。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    void DFS(int u,int fa,int deep[],vector<int> edge[]){
        deep[u]=deep[fa]+1;
        for(auto v:edge[u]){
            if (v==fa) continue;
            DFS(v,u,deep,edge);
        }
    }
    
    int dfs(int u,int fa,bool &qwq,int &ans,int deep[],vector<int> edge[],int st){
        int cnt=0;
        int aa=0;
        deep[u]=deep[fa]+1;
        for(auto v:edge[u]){
            if (v==fa) continue;
            ++cnt;
            aa+=dfs(v,u,qwq,ans,deep,edge,st);
        }
        if (!cnt){
            if (!(deep[u]&1)) qwq=true;
            return 1;
        }
        if (fa==st) ++aa;
        ans-=max(0,aa-1);
        return 0;
    }
    
    int main(void) {
        int n;
        read(n);
        vector<int> edge[n+1];
        for(int u,v,i=1;i<n;++i){
            read(u);
            read(v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        int deep[n+1]={0};
        DFS(1,1,deep,edge);
        int st=0,dest=0;
        for(int i=1;i<=n;++i)
            if (dest<deep[i]){
                dest=deep[i];
                st=i;
            }
        deep[st]=0;
        bool qwq=false;
        int ans=n-1;
        dfs(st,st,qwq,ans,deep,edge,st);
        printf("%d %d
    ",(qwq?3:1),ans);
        return 0;
    }
    


    C. Perfect Triples (CF 1338 C)

    题目大意

    有一个有无穷无尽个正整数的数组(s),其构造方法如下:

    • (a<b<c)
    • (a,b,c otin s)
    • (aigoplus bigoplus c=0)
    • (a,b,c)是可选中的字典序最小的一组
    • 把a,b,c依次加入(s)数组的末尾
    • 重复第一步

    现有(t)组询问,每组询问一个数(n),问你数组中第(n)个数(从(1)开始)是多少。

    解题思路

    打表大法好

    注意到(1igoplus 2igoplus 3=0)

    这恰好是(4)进制。于是我们可以把数转成四进制(二进制下俩俩合并),三个数看成一组,对于这一组的数,四进制下每一位我们就可以单独考虑。

    对于最高位,由于(a<b<c),所以最高位上一定是(1、2、3)

    然后对于以下的每一位,只有四种情况:

    • (0 0 0)
    • (1 2 3)
    • (2 3 1)
    • (3 1 2)

    这四种情况也是按照字典序从小到大排好的,其余的情况会出现重复数字。

    按照字典序从小到大构造的三元组的方法就很明显啦,对于三个数的每一位,依次取遍四种情况,然后到下一位。

    这也才造成我们打的表里面,第一个数以(4^n)个分组的结果。

    给定(n)就相当于问第(dfrac{n-1}{3})组(从(0)开始)的第((n-1)\%3)位是多少。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    void work(LL id,LL i,pair<LL,LL> &ans){
        if (i<0) return;
        if (i==0) {
            LL rank=id;
            if (rank==1){
                ans.first^=(1ll<<(i));
                ans.second^=(1ll<<(i+1));
            }else if (rank==2){
                ans.first^=(1ll<<(i+1));
                ans.second^=((1ll<<(i))^(1ll<<(i+1)));
            }else if (rank==3){
                ans.first^=((1ll<<(i))^(1ll<<(i+1)));
                ans.second^=(1ll<<(i));
            }
            return;
        }
        LL cnt=(1ll<<(i));
        work(id%cnt,i-2,ans);
        LL rank=id/cnt;
        if (rank==1){
            ans.first^=(1ll<<(i));
            ans.second^=(1ll<<(i+1));
        }else if (rank==2){
            ans.first^=(1ll<<(i+1));
            ans.second^=((1ll<<(i))^(1ll<<(i+1)));
        }else if (rank==3){
            ans.first^=((1ll<<(i))^(1ll<<(i+1)));
            ans.second^=(1ll<<(i));
        }
    }
    
    int main(void) {
        int t;
        read(t);
        while(t--){
            LL n;
            read(n);
            LL id=(n-1)/3;
            LL pos=(n-1)%3;
            pair<LL,LL> ans;
            ans.first=0;
            ans.second=0;
            for(int i=0;true;i+=2){
                if (id<(1ll<<i)){
                    work(id,i-2,ans);
                    ans.first^=(1ll<<(i));
                    ans.second^=(1ll<<(i+1));
                    break;
                }else id-=(1ll<<i);
            }
            printf("%lld
    ",pos==0?ans.first:(pos==1?ans.second:(ans.first^ans.second)));
        }
        return 0;
    }
    


    不能打(div2)但又虚(div1)......(qwq)

  • 相关阅读:
    C++中使用多线程
    hdu 4223 dp 求连续子序列的和的绝对值最小值
    hdu 1372 bfs 计算起点到终点的距离
    hdu 4217 线段树 依次取第几个最小值,求其sum
    心得
    hdu 1175 bfs 按要求进行搜索,是否能到达,抵消两个(相同)棋子
    hdu 4221 greed 注意范围 工作延期,使整个工作时间罚时最少的单个罚时最长的值
    hdu 2844 多重背包 多种硬币,每一种硬币有一点数量,看他能组成多少种钱
    uva LCDDisplay
    hdu 4218 模拟 根据一个圆点和半径画一个圆 注意半径要求
  • 原文地址:https://www.cnblogs.com/Lanly/p/12709501.html
Copyright © 2011-2022 走看看