• Codeforces Round #601 (Div. 2) 题解(A-F)


    传送门

    A. Changing Volume

    没啥好说的,方法很多,乱搞吧

    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x3f3f3f3f3f3f3f3f;
    int read(){
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    int T;
    
    int dfs(int a,int b){
    	if(a<0||b<0) return inf;
    	return abs(a-b);
    }
    
    int dfs2(int a,int b){
    	if(a<0||b<0) return inf;
    	if(a<b) swap(a,b);
    	int sub=a-b;
    	return min(sub/2+dfs(a-sub/2*2,b),sub/2+1+dfs(a-sub/2*2-2,b));
    }
    
    int dfs5(int a,int b){
    	if(a<0||b<0) return inf;
    	if(a<b) swap(a,b);
    	int sub=a-b;
    	return min(sub/5+dfs2(a-sub/5*5,b),sub/5+1+dfs2(a-sub/5*5-5,b));
    }
    
    int main(){
    	T=read();
    	while(T--){
    		int a=read(),b=read();
    		int ans=dfs5(a,b);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    B. Fridge Lockers

    题意解析

    (n) 个点和每个点的点权,在两点之间构建一条无向边的花费为两点的点权和,要求连 (m) 条无向边,使得每个顶点度 (geq 2)(n=2)是特殊情况。

    思路简述

    根据题意首先如果 (m<n) 或者 (n=2),答案直接 (-1)
    否则把 (n) 个点按点权排序,然后依次连成一个圈,然后将剩下的 (m-n) 条边全连到点权最小的两个点上去。

    代码

    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x3f3f3f3f3f3f3f3f;
    int read(){
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int MAXN=1e3+10;
    int T,n,m;
    struct Pair{
    	int v,id;
    }p[MAXN];
    
    bool cmp(Pair a,Pair b){
    	return a.v<b.v;
    }
    
    int main(){
    	T=read();
    	while(T--){
    		n=read(),m=read();
    		for(int i=1;i<=n;i++)
    			p[i].v=read(),p[i].id=i;
    		if(n<=2||m<n) {printf("-1
    ");continue;}
    		LL ans=0;
    		sort(p+1,p+n+1,cmp);
    		m-=n;
    		for(int i=1;i<=n;i++)
    			if(i<n) ans+=p[i].v+p[i+1].v;
    			else ans+=p[i].v+p[1].v;
    		for(int i=1;i<=m;i++)
    			ans+=p[1].v+p[2].v;
    		printf("%lld
    ",ans);
    		for(int i=1;i<=n;i++)
    			printf("%d %d
    ",i,i<n?i+1:1);
    		for(int i=1;i<=m;i++)
    			printf("%d %d
    ",p[1].id,p[2].id);
    	}
    	return 0;
    }
    

    C. League of Leesins

    题意

    将一个 (n) 的排列写成 (n-2) 个三元组,然后打乱每个元组里的数的顺序,再打乱 (n-2) 个三元组的顺序,然后要你根据这 (n-2) 个三元组还原出一个 (n) 的排列。

    思路

    基本乱搞,首先可以确定的是这个排列的第一个数、第二个数、倒数第二个数、最后一个数。
    然后根据第一个数、第二个数暴力的找这两个数出现的每个区间,从中提取出第三个数,然后根据第二、三个数,提取出第四个数……

    代码

    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x3f3f3f3f3f3f3f3f;
    int read(){
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int MAXN=1e5+10;
    int n,ans[MAXN],dead[MAXN];
    vector<int> vis[MAXN];
    vector<int> tur[MAXN];
    
    int findto(int now,int nxt){
    	for(int i=0;i<vis[now].size();i++)
    		for(int j=0;j<vis[nxt].size();j++){
    			int idnow=vis[now][i],idnxt=vis[nxt][j];
    			for(int q=0;q<3;q++)
    				for(int p=0;p<3;p++)
    					if(tur[idnow][q]==tur[idnxt][p]&&!dead[tur[idnow][q]])
    						return tur[idnow][q];
    		}
    }
    
    int main(){
    	n=read();
    	for(int i=1;i<=n-2;i++){
    		tur[i].push_back(read());
    		tur[i].push_back(read());
    		tur[i].push_back(read());
    		vis[tur[i][0]].push_back(i);
    		vis[tur[i][1]].push_back(i);
    		vis[tur[i][2]].push_back(i);
    	}
    	int now=0,tail=0;
    	for(int i=1;i<=n;i++)
    		if(vis[i].size()==1&&now==0) now=i;
    		else if(vis[i].size()==1) tail=i;
    	int nxt=0;
    	for(int i=0;i<3;i++)
    		if(vis[tur[vis[now][0]][i]].size()==2) nxt=tur[vis[now][0]][i];
    	for(int i=1;i<=n-2;i++){
    		ans[i]=now;
    		dead[now]=1;dead[nxt]=1;
    		if(i==n-2) break;
    		int to=findto(now,nxt);
    		now=nxt;nxt=to;
    	}
    	ans[n-1]=nxt;
    	ans[n]=tail;
    	for(int i=1;i<=n;i++)
    		printf("%d ",ans[i]);
    	return 0;
    }
    

    D. Feeding Chicken

    题意

    自认为比第三题简单,给你一张01图,要你划分 (k) 个连通块,要求这些连通块覆盖的最大值和最小值差距最小。

    思路

    因为数据范围太小,直接暴力就行了。
    先统计图中值的总和,然后取平均,取余,将余数分下去,这样就保证了 (k) 个连通块的差距最大为 (1)
    然后按照蛇形分配区域就行了。

    代码

    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    using namespace std;
    int T,n,m,k;
    int num[110];
    char key[110];
    char g[110][110];
    char ans[110][110];
    
    void solve(){
    	memset(num,0,sizeof(num));
    	memset(ans,0,sizeof(ans));
    	scanf("%d%d%d",&n,&m,&k);
    	for(int i=1;i<=n;i++)
    		scanf("%s",*(g+i)+1);
    	int sum=0;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(g[i][j]=='R')
    				sum++;
    	int ave=sum/k,more=sum%k;
    	for(int i=1,pt=1;i<=n;i++){
    		if(i%2==1)
    			for(int j=1;j<=m;j++){
    				if(g[i][j]=='R'&&(pt<=more&&num[pt]==ave+1||pt>more&&num[pt]==ave)) pt++;
    				ans[i][j]=key[pt];
    				if(g[i][j]=='R') num[pt]++;
    			}
    		else
    			for(int j=m;j>=1;j--){
    				if(g[i][j]=='R'&&(pt<=more&&num[pt]==ave+1||pt>more&&num[pt]==ave)) pt++;
    				ans[i][j]=key[pt];
    				if(g[i][j]=='R') num[pt]++;
    			}
    	} 
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++)
    			printf("%c",ans[i][j]);
    		printf("
    ");
    	}
    		
    }
    
    int main(){
    	for(int i=1;i<=26;i++) key[i]='a'+i-1;
    	for(int i=27;i<=52;i++) key[i]='A'+i-27;
    	for(int i=53;i<=62;i++) key[i]='0'+i-53;
    	scanf("%d",&T);
    	while(T--) solve();
    	return 0;
    }
    

    E1. Send Boxes to Alice (Easy Version)

    题意

    给含有 (n) 个元素的数列 (a(0leq a_ileq 1)),可以将 (a_i) 的值加到 (a_j) 上,花费 (a_i imes|i-j|),要求最小的花费使得 (a_i\% x=0(1leq ileq n,xin Z))

    思路

    首先求出 (sum=sum_1^na_i)(x) 一定是 (sum) 的质因数之一。
    然后选出每一段和为 (x) 的连续区间,选出其中值为 (1) 的位置,取其中位数,
    然后算出将该区间中所有位置的值放在中位数上的花费,计算出所有区间的花费相加就是最小花费了。

    代码

    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <vector>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e6+10;
    int n,a[MAXN],sum=0;
    LL ans=0x3f3f3f3f3f3f3f3f;
    vector<int> num;
    vector<int> pri;
    
    void calc(int x){
    	LL cnt=0;
    	for(int i=1;i<=n;i++){
    		if(a[i]==1&&num.size()<x) num.push_back(i);
    		if(num.size()==x){
    			for(int j=0;j<num.size();j++)
    				cnt+=abs(num[j]-num[x/2]);
    			num.clear();
    		}
    	}
    	ans=min(ans,cnt);	
    }
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]),
    		sum+=a[i];
    	if(sum==0) {printf("0
    ");return 0;}
    	if(sum==1) {printf("-1
    ");return 0;}
    	int temp=sum;
    	for(int i=2;i*i<=temp;i++)
    		if(temp%i==0){
    			pri.push_back(i);
    			while(temp%i==0) temp/=i;
    		}
    	if(temp>1) pri.push_back(temp);
    	for(int i=0;i<pri.size();i++) calc(pri[i]);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    E2. Send Boxes to Alice (Hard Version)

    题意

    这道题比起前一道题,增大了 (n) 的范围(没啥影响),放开了对 (a_i(0leq a_ileq 10^6)) 的限制,这是我不由得想起之前做的均分纸牌的问题。

    思路

    同样枚举 (sum) 的质因数 (x),对于每个 (x),计算 (a_i) 的前缀和 (sum_i)
    因为要求 (a_i\%x=0),所以 (sum_i\%x=0)
    如果 (sum_i\% x ot=0),对于余数,可以给他凑成 (x),也可以直接把它放到之后考虑,
    对于这两种手段,取花费最少的一个。然后算总和取最小值就行了

    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <math.h>
    using namespace std;
    typedef long long LL;
    const int inf=0x3f3f3f3f;
    const LL INF=0x7f7f7f7f7f7f7f7f;
    const int MAXN=1e6+10;
    int n,a[MAXN];
    LL sum=0,ans=INF;
    
    void calc(LL x){
    	LL cnt=0,now=0;
    	for(int i=1;i<=n;i++){
    		(now+=a[i])%=x;
    		cnt+=min(now,x-now);
    	}
    	ans=min(cnt,ans);
    }
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]),sum+=a[i];
    	if(sum==0) {printf("0
    ");return 0;}
    	if(sum==1) {printf("-1
    ");return 0;}
    	for(LL i=2;i*i<=sum;i++)
    		if(sum%i==0){
    			calc(i);
    			while(sum%i==0) sum/=i;
    		}
    	if(sum>1) calc(sum);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    F. Point Ordering

    题意

    交互题,已知有一个 (n) 顶点的凸多边形,你可以提出不超过 (3cdot n) 个问题,问题类型格式和回答内容如下:
    1类问题:1 i j k,回答:编号为i、j、k的点构成的三角形的面积
    2类问题:2 i j k,回答:(vec{ij})(vec{ik}) 的叉积,即 (x_{ij}cdot y_{ik}-y_{ij}cdot x_{ik})
    要求你逆时针输出凸多边形的点的编号。

    思路

    首先可以搞懂 (2) 问题的本质,如果 (vec{ij})(vec{ik}) 旋转为顺时针方向,则回答为 (-1),否则回答为 (1)
    先可以提 (n)(2) 问题,找到点 (1) 逆时针的下一个点 (nxt)
    然后提 (n)(1) 问题,算出 (S_{1,nxt,i}(i ot=1,i ot=nxt)),然后按 (S) 从小到大排序
    最后建立一个链表,以刚刚的顺序将每个点插入,这里要再提 (n)(2) 问题判断一下:
    设上一个插入的点的编号为 (now),将插入点 (i) 那么:
    如果当前回答为 (-1),则将点 (i) 插入 (now) 的前面
    如果当前回答为 (1),则将 (i) 插入 (now) 后面
    将所有点插入之后就完成了~~~

    代码

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    int pre[1010],nxt[1010];
    struct Pair{
    	int id;
    	LL area;
    }p[1010];
    int n,cnt=0;
    
    bool cmp(Pair a,Pair b){
    	return a.area<b.area;
    }
    
    int main(){
    	scanf("%d",&n);
    	int now=2;
    	for(int i=3,x;i<=n;i++){
    		printf("2 1 %d %d
    ",now,i);fflush(stdout);
    		scanf("%d",&x);
    		if(x==-1) now=i;
    	}
    	nxt[1]=now;
    	pre[now]=1;
    	for(int i=2;i<=n;i++){
    		if(i==now) continue;
    		p[++cnt].id=i;
    		printf("1 1 %d %d
    ",now,i);fflush(stdout);
    		scanf("%lld",&p[cnt].area);
    	}
    	sort(p+1,p+cnt+1,cmp);
    	for(int i=1,x;i<=cnt;i++){
    		int id=p[i].id;
    		printf("2 1 %d %d
    ",now,id);fflush(stdout);
    		scanf("%d",&x);
    		if(x==1){
    			pre[id]=now;nxt[id]=nxt[now];
    			pre[nxt[now]]=id;nxt[now]=id;
    		}
    		else{
    			nxt[id]=now;pre[id]=pre[now];
    			nxt[pre[now]]=id;pre[now]=id;
    		}
    		now=id;
    	}
    	printf("0 ");
    	for(int i=1;i!=0;i=nxt[i])
    		printf(" %d",i);
    	fflush(stdout);
    	return 0;
    }
    
  • 相关阅读:
    txt换行追加写入
    np.unique( )的用法
    生成自己想要的任意颜色的图片
    183. 木材加工
    575. 字符串解码
    364. 接雨水 II
    255. Multi-string search
    433. 岛屿的个数
    591. 连接图 III
    918. 三数之和
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/11905096.html
Copyright © 2020-2023  润新知