ICPC2020-济南
C
Description
给定n个堆,每个堆的石子个数在[1,3],合并两个堆(大小分别为x,y)的代价为(x mod 3)(y mod 3)。求合并成一堆的最小代价。
Solution
显然如果堆的大小是3的倍数时,无论怎么合并,对代价的贡献都是0。
所以可以直接忽略大小为3的倍数的堆,考虑mod 3=1,2的堆。
显然,合并(1,2)直接可以获得一个3的倍数,所以尽可能合并(1,2).
对于剩下来的1/2,三个一组合并,又可以得到一个3的倍数。然后对最后剩下的堆直接合并即可。
#include<bits/stdc++.h>
using namespace std;
int t[5],ans;
int main(){
for(int i=1;i<=3;++i)
scanf("%d",&t[i%3]);
if(t[1]&&t[2]){
int tmp=min(t[1],t[2]);
ans+=2*tmp;
t[1]-=tmp;t[2]-=tmp;t[0]+=tmp;
}
if(t[1]>1){
int tmp=t[1]/3;
ans+=3*tmp;
t[1]%=3;
t[0]+=tmp;
if(t[1]>1) ans+=1;
}
if(t[2]>1){
int tmp=t[2]/3;
ans+=6*tmp;
t[2]%=3;
t[0]+=tmp;
if(t[2]>1) ans+=4;
}
printf("%d\n",ans);
return 0;
}
D
Description
每个学生的作业的字数范围在\([l_i,r_i]\),每个学生的得分与他的字数在班上的排名有关,越多排名越高。
设学生的初始排名为大家都写\(r_i\)个字的情况下的排名,求在排名不变差的情况下的最小总字数。
Solution
初始排名按\(r_i\)排序即可得到。
对于一个学生,只需要不比 所有排名不比他高的人 写得少即可。
即取max{ max{ 排名比他低的人能接受的最少字数 },max{ 排名和他相同的人的\(l_i\) } }。
那么按\(r_i\)从小到达扫一遍确定每个人能接受的最少字数即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
struct student{
int l,r;
}a[N];
int n;
long long ans;
bool cmp(student a,student b){
if(a.r!=b.r) return a.r<b.r;
return a.l>b.l;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d%d",&a[i].l,&a[i].r);
sort(a+1,a+1+n,cmp);
for(int i=1,l=0;i<=n;++i){
if(a[i].r!=a[i-1].r)
l=max(l,a[i].l);
ans+=l;
}
printf("%lld\n",ans);
return 0;
}
G
Description
给定两个数x,y(x>y),每次可以将x异或上一个不大于当前x的数。求一种小于五步的方案使x变成y。
Solution
很显然,将x异或到当前位数的全1状态所需的数<x(因为所需的数在当前x的位数下最高位是0,所以必然<x),那么我们先将x变成位数不变的全1态。
然后我们只需异或一个与y的0/1相反的数即可。显然这个数小于全1态。
#include<bits/stdc++.h>
using namespace std;
long long x,y,ans;
int main(){
scanf("%lld%lld",&x,&y);
printf("2\n");
for(int i=0;(1ll<<i)<=x;++i){
if(!((1ll<<i)&x)) ans|=(1ll<<i);
}
printf("%lld ",ans);
ans=0ll;
for(int i=0;(1ll<<i)<=x;++i){
if(!((1ll<<i)&y)) ans|=(1ll<<i);
}
printf("%lld\n",ans);
return 0;
}
J
Description
给定一棵树,求一种给每个点赋值\(a_i\)的方案使得存在一条边(x,y)的充要条件是\(a_x\;or\;a_y=2^{60}-1\)。
Solution
构造题。
观察可以发现层数同奇偶的层之间必不连。那么可以对奇偶层设置不同的后缀01/10来保证同奇偶的层之间不连。
可以证明的是,min{奇数层节点个数,偶数层节点个数} \(\leq\frac{n}{2}\)。
所以我们可以对个数少的那个部分用二进制某位x取0来进行连边约束:只有能连向该点的节点的二进制位x取1。
这样就可以保证连边之间\(a_x\;or\;a_y=2^{60}-1\),不连的点之间\(a_x\;or\;a_y<2^{60}-1\)
#include<bits/stdc++.h>
using namespace std;
const int N=105;
struct graph{
int nxt,to;
}e[N<<1];
int g[N],dep[N],n,cnt,tot=2;
long long ans[N];
void adde(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
void dfs(int u){
if(dep[u]&1) ++cnt;
ans[u]=(1<<(dep[u]&1));
for(int i=g[u];i;i=e[i].nxt)
if(!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
dfs(e[i].to);
}
}
void dfs1(int u,bool flag){
if(flag){
++tot;
ans[u]|=((1ll<<60)-1ll-(1ll<<tot)-3);
}
for(int i=g[u];i;i=e[i].nxt)
if(dep[e[i].to]>dep[u]){
dfs1(e[i].to,!flag);
}
}
void dfs2(int u,bool flag){
if(!flag){
long long tmp=0;
for(int i=g[u];i;i=e[i].nxt)
tmp|=(((1ll<<60)-1ll)^ans[e[i].to]);
tmp-=(tmp&3);
ans[u]|=tmp;
}
for(int i=g[u];i;i=e[i].nxt)
if(dep[e[i].to]>dep[u]){
dfs2(e[i].to,!flag);
}
}
int main(){
scanf("%d",&n);
for(int i=1,x,y;i<n;++i){
scanf("%d%d",&x,&y);
adde(x,y);adde(y,x);
}
cnt=0;dep[1]=1;dfs(1);
if(cnt<=(n>>1)){
dfs1(1,true);
dfs2(1,true);
}
else{
dfs1(1,false);
dfs2(1,false);
}
for(int i=1;i<=n;++i)
printf("%lld ",ans[i]);
return 0;
}
M
Description
经典小学烙饼问题。有n个饼,每个饼的两面都要烙,可以同时烙k个。求最少需要烙几个饼。
Solution
先把所有的第一面烙了,再烙第二面。然后最后一批第一面可能会剩位置给最早烙好的第一面。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,k;
scanf("%d%d",&n,&k);
int fro=n,back=0;
int ans=0;
ans+=fro/k;back+=fro/k*k;fro%=k;
++ans;back-=min(back,k-fro);back+=fro;fro=0;
ans+=back/k;back%=k;
if(back) ++ans;
printf("%d\n",ans);
return 0;
}