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\n",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\n");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\n",ans);
		for(int i=1;i<=n;i++)
			printf("%d %d\n",i,i<n?i+1:1);
		for(int i=1;i<=m;i++)
			printf("%d %d\n",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("\n");
	}
		
}

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(0\leq a_i\leq 1)\),可以将 \(a_i\) 的值加到 \(a_j\) 上,花费 \(a_i\times|i-j|\),要求最小的花费使得 \(a_i\% x=0(1\leq i\leq n,x\in 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\n");return 0;}
	if(sum==1) {printf("-1\n");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\n",ans);
	return 0;
}

E2. Send Boxes to Alice (Hard Version)

题意

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

思路

同样枚举 \(sum\) 的质因数 \(x\),对于每个 \(x\),计算 \(a_i\) 的前缀和 \(sum_i\)
因为要求 \(a_i\%x=0\),所以 \(sum_i\%x=0\)
如果 \(sum_i\% x\not=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\n");return 0;}
	if(sum==1) {printf("-1\n");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\n",ans);
	return 0;
}

F. Point Ordering

题意

交互题,已知有一个 \(n\) 顶点的凸多边形,你可以提出不超过 \(3\cdot 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\not=1,i\not=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\n",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\n",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\n",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;
}
posted @ 2019-11-21 13:40  BakaCirno  阅读(191)  评论(0编辑  收藏  举报