Codeforces Round #595 (Div. 3) 题解

Posted bakacirno

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
康康代码

 

以上是关于Codeforces Round #595 (Div. 3) 题解的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #595 (Div. 3) 题解

Codeforces Round #595 (Div. 3)

Codeforces Round #595 (Div. 3)

Codeforces Round #595 (Div. 3)B2 简单的dfs

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

Codeforces Round #595 (Div. 3) E. By Elevator or Stairs?