• Codeforces Round #595 (Div. 3) 题解


    A. Yet Another Dividing into Teams

    传送门

    签到,有相邻的数字 ans=2,否则 ans=1

    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n);
            memset(vis,0,sizeof(vis));
            for(int i=1,x;i<=n;i++) scanf("%d",&x),vis[x]=1;
            int flag=0;
            for(int i=1;i<=100;i++) if(vis[i]&&(vis[i-1]||vis[i+1])) {flag=1;break;}
            if(flag==0) printf("1
    ");
            else printf("2
    ");
        }
        return 0;
    }

    B. Books Exchange

    传送门

    找每个顶点处在的环的大小,dfs 行了

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <queue>
    #include <utility>
    #define MAXN 200010
    using namespace std;
    const int inf=0x3f3f3f3f;
    int ans=inf;
    int T,n,to[MAXN],len[MAXN],vis[MAXN];
    
    void dfs(int u,int dis){
        if(vis[u]) {len[u]=dis;return;}
        vis[u]=1;
        dfs(to[u],dis+1);
        len[u]=len[to[u]];
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            memset(vis,0,(n+1)*sizeof(int));
            memset(len,0,(n+1)*sizeof(int));
            for(int i=1;i<=n;i++) scanf("%d",&to[i]);
            for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
            for(int i=1;i<=n;i++) printf("%d ",len[i]);
            printf("
    ");
        }
        
        return 0;
    }

    C. Good Numbers

    传送门

    我的方法是这样的,先将这个数转化为三进制来看。

    从 0 位看到最高位,如果第 i 位是 2, 那么就从 i+1 位到更高的位去找一个是 0 的位 j, 将其变成 1, 然后将 j-1 到 0 位的数字全变成 0.

    这样扫描一遍之后, 将这个三进制数转化为十进制就是答案了.

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <algorithm>
    #include <utility>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #define MAXN 100010
    #define mid ((l+r)>>1)
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x3f3f3f3f3f3f3f3f;
    int T;
    LL n;
    int num[100];
    
    
    
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%lld",&n);
            memset(num,0,sizeof(num));
            LL k=1,index=0;
            for(;k<n;k*=3,index+=1);
            while(k){
                while(n>=k) n-=k,num[index]++;
                k/=3,index-=1;
            }
            for(int i=0;i<=64;i++){
                if(num[i]==2){
                    for(int j=i+1;j<=64;j++){
                        if(num[j]==0){
                            num[j]=1;
                            for(int k=j-1;k>=0;k--) num[k]=0;
                            break;
                        }
                    }
                }
            }
            LL ans=0;
            k=1;
            for(int i=0;i<=64;i++) {
                if(num[i]) ans+=k;
                k*=3;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    康康代码

    D. Too Many Segments

    传送门

    我用的贪心+线段树, 结束后发现大神们都用的是优先队列啊, set什么的......

    首先我们想要保留尽量多的区间, 那么就要剩下的区间尽量少的重叠, 那么根据这个性质我们可以对区间进行先按右端点从小到大, 若右相等则按左从小到大这样排序.

    然后依次插入这些区间, 若一个区间满足这个区间内的点的最大被覆盖次数还不足 k, 那么就将这个区间覆盖下去, 否则这个区间就将被删去, 加入答案. 这个过程需要区间加和查询区间最大值, 用线段树.

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <algorithm>
    #include <utility>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #define MAXN 200010
    #define mid ((l+r)>>1)
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x3f3f3f3f3f3f3f3f;
    int n,k;
    struct Seg{
        int l,r,id;
    }p[MAXN];
    vector<int> ans;
    
    struct SegmentTree{
        int tag[MAXN*4],maxv[MAXN*4];
        
        void build(){memset(tag,0,sizeof(tag));memset(maxv,0,sizeof(maxv));}
        
        void pushdown(int id,int l,int r){
            maxv[id<<1]+=tag[id];tag[id<<1]+=tag[id];
            maxv[id<<1|1]+=tag[id];tag[id<<1|1]+=tag[id];
            tag[id]=0;
        }
        
        int update(int id,int l,int r,int L,int R,int x){
            if(L<=l&&r<=R){maxv[id]+=x;tag[id]+=x;return 0;}
            if(tag[id]) pushdown(id,l,r);
            if(L<=mid) update(id<<1,l,mid,L,R,x);
            if(R>mid) update(id<<1|1,mid+1,r,L,R,x);
            maxv[id]=max(maxv[id<<1],maxv[id<<1|1]);
        }
        
        int ask(int id,int l,int r,int L,int R){
            if(L<=l&&r<=R) return maxv[id];
            if(tag[id]) pushdown(id,l,r);
            int res1=0,res2=0;
            if(L<=mid) res1=ask(id<<1,l,mid,L,R);
            if(R>mid) res2=ask(id<<1|1,mid+1,r,L,R);
            return max(res1,res2);
        }
    }tree;
    
    bool cmp(Seg a,Seg b){
        return a.r<b.r||a.r==b.r&&a.l<b.l;
    }
    
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d%d",&p[i].l,&p[i].r),p[i].id=i;
        sort(p+1,p+n+1,cmp);
        tree.build();
        for(int i=1;i<=n;i++){
            if(tree.ask(1,1,MAXN,p[i].l,p[i].r)<k) tree.update(1,1,MAXN,p[i].l,p[i].r,1);
            else ans.push_back(p[i].id);
        }
        printf("%d
    ",ans.size());
        for(int i=0;i<ans.size();i++) printf("%d ",ans[i]);
        return 0;
    }
    康康代码

    E. By Elevator or Stairs?

    传送门

    这道题其实比 C 题还要简单一点, 就是一个 dp 水题

    走了楼梯再做电梯就要等 c 时间, 那么设两个状态, f1i 是走楼梯到达第 i 楼用的最少时间, f2i 是坐电梯到达第 i 楼用的最少时间, 很容易得到状态转移方程:

    f1i = min(f1i-1, f2i-1) + a[i-1] , 走楼梯到达第 i 层的最少时间是到达第 i-1 层的最少时间 + a[i -1].

    f2i = min(f2i-1, f1i-1 + c) + b[i-1] , 做电梯到达第 i 层的最少时间是坐电梯到达第 i - 1层的最小时间和走楼梯到 i-1 层的最小时间 + 等电梯的时间这两者中的较小值 + b[i - 1]

    注意初态 f11 = 0, f21 = inf, 因为一楼不可能是坐电梯到达的嘛.

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAXN 200010
    using namespace std;
    const int inf=0x3f3f3f3f;
    int n,c,a[MAXN],b[MAXN];
    int f1[MAXN],f2[MAXN];
    
    int main(){
        scanf("%d%d",&n,&c);
        for(int i=1;i<n;i++) scanf("%d",&a[i]);
        for(int i=1;i<n;i++) scanf("%d",&b[i]);
        f1[1]=0;f2[1]=inf;
        for(int i=2;i<=n;i++){
            f1[i]=min(f2[i-1],f1[i-1])+a[i-1];
            f2[i]=min(f2[i-1],f1[i-1]+c)+b[i-1];
        }
        for(int i=1;i<=n;i++) printf("%d ",min(f1[i],f2[i]));
        return 0;
    }
    康康代码

    F. Maximum Weight Subset

    传送门

    介绍一个O(N3)的树形Dp方法.

    设状态 f[i,j] 是点 i 到以它为根节点的子树中最近一个选中点的距离大于等于 j 时这颗子树产生的最大贡献.

    那么对于 f[i,0] 就是选中点 i 时的最大值,因为每个选中点之间距离要大于 k, 那么转移方程为

    $$fleft[ i,0 ight] =aleft[ i ight] +sum ^{son}_{t}fleft[ t,k ight]$$

    对于 f[i,w] (0<w<k), 枚举 i 的一个儿子离 i 最近, 它的最近选中点距离当然为 w-1, 为了满足选中点距离大于 k, 并且其他儿子中最近选中点距离 i 不能小于 w, 那么可以得出转移方程:

    $$max _{sin son}left{ fleft[ s,w-1 ight] +sum ^{son}_{t eq s}left[ t,max left( k-w,w-1 ight) ight] ight}$$

    那么注意f[i,j] = max(f[i,j] , f[i,j+1])

    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    using namespace std;
    int n,k,a[210],f[210][210];
    int head[210],to[410],nxt[410],tot=1;
    
    void add(int u,int v){to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
    
    void dfs(int u,int rt){
        f[u][0]=a[u];
        for(int i=head[u];i;i=nxt[i]){
            if(to[i]==rt) continue;
            dfs(to[i],u);
        }
        for(int i=head[u];i;i=nxt[i]){
            if(to[i]==rt) continue;
            f[u][0]+=f[to[i]][k];
        } 
        for(int w=1;w<=k;w++){
            for(int i=head[u];i;i=nxt[i]){
                if(to[i]==rt) continue;
                int temp=f[to[i]][w-1];
                for(int j=head[u];j;j=nxt[j]){
                    if(to[j]==rt||to[j]==to[i]) continue;
                    temp+=f[to[j]][max(k-w,w-1)];
                }
                f[u][w]=max(f[u][w],temp);
            }
        }
        for(int w=k;w>=0;w--) f[u][w]=max(f[u][w+1],f[u][w]);
    }
    
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1,u,v;i<n;i++){scanf("%d%d",&u,&v);add(u,v);add(v,u);}
        dfs(1,0);
        cout<<f[1][0]<<endl;
        return 0;
    }
    康康代码
  • 相关阅读:
    ngix反向代理-之反向
    redux和flux究竟有什么不同, 说点自己的理解
    npm发包记录
    由一个聚焦-focus-事件异常跟踪引起的总结
    git查看分支的几个方法
    test-your-mind-快速测试自己的代码
    contos7 yum安装php7.2与swoole (2)
    php_ thinkphp 时间回滚
    30个php操作redis常用方法代码例子
    redis_php 安装与卸载
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/11725407.html
Copyright © 2020-2023  润新知