Codeforces Round #398 (div.2)简要题解
这场cf时间特别好,周六下午,于是就打了打(谁叫我永远1800上不去div1)
比以前div2的题目更均衡了,没有太简单和太难的...好像B题难度高了很多,然后卡了很多人。
然后我最后做了四题,E题感觉不会太难就是切不下来,还好前面几题A的快居然混了个RANK6,然后+204,终于上到紫名了......
---------我是分割线君
A.Snacktower
有一个1到n的全排列,按顺序每一秒会到达一个数,你要把它们从n到1叠起来,如果能叠,你就马上叠。
比如n=3到达顺序312 ,那么你第一秒就叠3,第三秒叠21。
没什么好说的,直接模拟。n<=10^5
看到题目直接就写了个堆,根本没必要,好傻。
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<map> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } priority_queue<int> q; int n,nown; void check() { while(!q.empty()&&q.top()==nown) { printf("%d ",q.top()); q.pop();nown--; } } int main() { n=read();nown=n; for(int i=1;i<=n;i++) { int x=read(); if(x==nown) printf("%d ",x),nown--; else q.push(x); check(); printf("\n"); } return 0; }
B.The Queue
有一个只从ts开到tt的地方,很多人来,每个人要呆t分钟做事情。一个人来的时候如果有人在做事情,他就排队。你是一个谦让的人,会让同时到的人排前面或者先做事情,问你从什么时候来等候时间最小。
这道题卡了很多人.....
很显然,直接枚举在那一个人的前一秒来,或者所有人之后来就可以了。
对于开门之前的时候,设你在第i个人之前1s到,等候时间为(i-1)*t+(ts-你到的时间);
对于开门之后的时候,维护队伍,以及从什么时候开始排队,设有tot个人排队,开始排队的时间为begin,等候时间为t*tot+begin-s[i]
这样就没了,考的就是细节。
还好写一次就过了,这题拉了挺多分......
复杂度On
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<map> #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } ll s[100005]; int n; ll tf,tt,t; ll beg,tot; ll ans=200000000000000LL,times=0; int main() { tf=read();tt=read();t=read();n=(int)read();tt-=t; for(int i=1;i<=n;i++) s[i]=read(); int i=1; s[0]=-1; for(;s[i]<tf&&i<=n;i++) { if(s[i]==s[i-1]) continue; if(ans>tf-(s[i]-1)+t*(i-1)) { ans=tf-(s[i]-1)+t*(i-1);times=s[i]-1; } } beg=tf;tot=i-1; for(;i<=n&&s[i]<=tt;i++) { if(s[i]!=s[i-1]) { if(beg+tot*t<=s[i]-1) { printf("%I64d\n",s[i]-1); return 0; } else { if(beg+tot*t-(s[i]-1)<ans) { ans=beg+tot*t-(s[i]-1); times=s[i]-1; } } } if(beg+tot*t<=s[i]) { beg=s[i];tot=1; } else tot++; } if(beg+tot*t<=tt) { printf("%I64d\n",beg+tot*t); } else printf("%I64d\n",times); return 0; }
C.Garland
有一棵给定根的树,有点权,要求剪掉两条边使得三个联通块权值和相等。n<=1000000
很水的题目,直接dfs,用S[i]表示点i和它的子树的权值和,能分就分了。
最后判断一下即可。
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<map> #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,rt,cnt=0; struct edge{ int to,next; }e[4000005]; int head[2000005]; int tot=0,has=0; int s[2000005]; int ans1=-1,ans2=-1; inline void ins(int f,int t) { e[++cnt].next=head[f]; head[f]=cnt; e[cnt].to=t; } inline void insw(int f,int t){ins(f,t);ins(t,f);} void findans(int x) { if(has>2) return; else has++; if(ans1==-1) ans1=x; else if(ans2==-1) ans2=x; } void dfs(int x,int fa) { for(int i=head[x];i>0;i=e[i].next) { int v=e[i].to; if(v!=fa) { dfs(v,x); if(s[v]==tot) findans(v); else s[x]+=s[v]; } } } int main() { n=read(); for(int i=1;i<=n;i++) { int u=read();s[i]=read(); if(u==0) rt=i; else insw(i,u); tot+=s[i]; } if(tot%3!=0) return 0*puts("-1"); else tot/=3; dfs(rt,0); if(has>=2) printf("%d %d\n",ans1,ans2); else puts("-1"); return 0; }
D.Cartons of milk
冰箱有n瓶牛奶,商店有m瓶牛奶。
每瓶牛奶一个保质期Ai,然后必须在保质期前喝完它,每天只能喝k瓶牛奶。求最多可以从商店买几瓶。n,m<=10^6,Ai<=10^7
Ai范围很小,从后往前,让每一瓶牛奶尽可能晚喝掉,看看能否喝完冰箱里的。
如果能喝完,就把商店的牛奶排序一下,从后往前对每一个还能喝的天,都尽可能的买,即可。复杂度(mlogm+Ai)
应该有更简单的做法,我这个做法是现场想的,能做就直接做了。
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<map> #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m,maxn,k; int f[10000005]; struct gg{ int num,x; }s[10000005]; int ans[1000005]; int sum=0; bool cmp(gg x,gg y) { return x.x<y.x; } int main() { n=read();m=read();k=read(); for(int i=1;i<=n;i++) { int x=read();++f[x]; maxn=max(maxn,x); } for(int i=1;i<=m;i++) { s[i].x=read();s[i].num=i; } sort(s+1,s+m+1,cmp); for(int i=maxn;i;i--) { if(f[i]>k) { f[i-1]+=f[i]-k;f[i]=0; } else f[i]=k-f[i]; } if(f[0]>k) return 0*puts("-1"); else f[0]=k-f[0]; int j=m; for(int i=maxn+1;i<=10000000;i++) f[i]=k; for(int i=10000000;i>=0;--i) { while(j>=1&&s[j].x>=i&&f[i]--) { ans[++sum]=s[j].num;j--; } } printf("%d\n",sum); for(;sum;--sum)printf("%d ",ans[sum]); return 0; }
E. Change-Free
一个人无限的纸币(100快)和有限的硬币(1块),他要在n天内每天买不同价格的产品Si,每天服务员都有一个不满意因数Wi,每次交易服务员会有一个不满意度
为Wi*(找回的纸币的数量+找回的硬币的数量)。求一个付钱方案让总不满意度最小。n<=100000
很显然每天要考虑怎么付钱的部分只有 Si%100,并且要不然就全部硬币付,要不然就一张纸币甩出来。
其实这道题真的不难,但我就是没发现一个性质,就是每一天付硬币和不付硬币,硬币的差值是100......
知道了这个就很简单了。
我们从第一天到最后一天,每天都先购买它,然后把不购买会产生的不满意度存到堆里面。
如果发现钱不够了,就去堆里找一个最小值,并且在那一天用纸币付钱,换取100个硬币。最后输出就行了。复杂度nlogn
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<map> #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m; int s[100005]; bool flag[100005]; int w[100005]; ll ans=0; struct node{ ll f,num; friend bool operator < (node x,node y) { return x.f>y.f; } }newx; priority_queue<node> q; int main() { n=read();m=read(); for(int i=1;i<=n;i++) s[i]=read(); for(int i=1;i<=n;i++) w[i]=read(); for(int i=1;i<=n;i++) { if(s[i]%100==0) continue; m-=s[i]%100; ll x=(ll)(100-(s[i]%100))*w[i]; q.push((node){x,i}); while(m<0) { newx=q.top();q.pop(); flag[newx.num]=1; m+=100; ans+=newx.f; } } printf("%I64d\n",ans); for(int i=1;i<=n;i++) { if(s[i]%100==0) printf("%d 0\n",s[i]/100); else if(flag[i]) printf("%d 0\n",s[i]/100+1); else printf("%d %d\n",s[i]/100,s[i]%100); } return 0; }
最后附上成绩图。