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;
}