Educational Codeforces Round 37
A - Water The Garden
/* 给出一排格子,告诉你一些格子上有水,会往两边流动,问流满全部格子需要的时间 */ #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> using namespace std; int main(){ int T,n,m,a[210],d[210],ans; scanf("%d",&T); while(T--){ ans=0; scanf("%d%d",&n,&m); memset(d,0x3f,sizeof(d)); for(int i=1;i<=m;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ d[i]=min(d[i],abs(i-a[j])+1); }ans=max(ans,d[i]); } printf("%d\n",ans); }return 0; }
B - Tea Queue
/* 告诉你n个人来打水的时间和等待到的最迟时间,来的时间相同的话编号小的排前面 问每个人取到水的时间,如果没取到水,则输出0 */ #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int N=5010; int T,n,l[N],r[N],ans[N]; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]); int nowt=0; for(int i=1;i<=n;i++){ if(nowt>r[i]){ans[i]=0;continue;} if(l[i]>=nowt)ans[i]=l[i],nowt=l[i]+1; else ans[i]=nowt,nowt++; }for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' '); }return 0; }
C - Swap Adjacent Elements
/* 告诉你一个a序列,和一个s序列,s序列为1表示这个位置的a序列可以和下一个互换, 问能否通过随意交换可互换元素将a序列排序 */ #include <cstdio> #include <algorithm> using namespace std; int n,a[200010]; char s[200010]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); scanf("%s",s+1);s[n]='0'; for(int i=1;i<n;i++){ if(s[i]=='1'){ for(int j=i;j<=n;j++){ if(s[j]=='0'){ sort(a+i,a+j+1); i=j; break; } } } } int flag=1; for(int i=1;i<n;i++){ if(a[i]>a[i+1]){puts("NO");flag=0;break;} }if(flag)puts("YES"); return 0; }
D - Tanks
/* 题目大意:给出N个容量无限的桶,每个桶中开始都有一些水,给出一个大小为K的勺子, 问你能否通过N+5次以内的勺水操作(从一个桶到另一个桶),使得最后能有一个桶中的 水为V 题解:我们将所有的桶中的水量对K取模,如果这些值能够取一些相加取模后得到V%K, 说明答案可行,我们可以用01背包解决这个问题,对于路径输出,我们在做dp的时候记录路径, 表示这个位置是答案的组成部分,那么根据是否是答案的组成部分,我们将桶分为两部分, 对于处于同个部分的桶,我们把水全都倒进一个桶中, 那么最后只要在这两个桶相互倒就可以得到答案 */ #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int MAXN=5010; int N,K,V,S,a[MAXN]; bool dp[MAXN][MAXN],pre[MAXN][MAXN]; vector<int> vc[2]; void Solve(){ for(int i=1;i<=N;i++)S+=a[i]; if(S<V){puts("NO");return;} dp[0][0]=1; for(int i=1;i<=N;i++){ int x=a[i]%K; for(int j=0;j<K;j++)if(dp[i-1][j]){ dp[i][j]=1; pre[i][j]=0; dp[i][(j+x)%K]=1; pre[i][(j+x)%K]=1; } }if(!dp[N][V%K]){puts("NO");return;} int v=V%K; for(int i=N;i>0;i--){ int x=a[i]%K; if(pre[i][v]){ vc[1].push_back(i); v=(v-x+K)%K; }else vc[0].push_back(i); } puts("YES"); sort(vc[0].begin(),vc[0].end()); sort(vc[1].begin(),vc[1].end()); for(int k=0;k<2;k++)if(vc[k].size()){ for(int i=1;i<vc[k].size();i++)printf("%d %d %d\n",1000000000,vc[k][i],vc[k][0]); } int x0,x1,s=0; for(int i=0;i<vc[1].size();i++)s+=a[vc[1][i]]; if(!vc[0].size())x1=vc[1][0],x0=vc[1][1]; else if(!vc[1].size())x1=vc[0][1],x0=vc[0][0]; else x1=vc[1][0],x0=vc[0][0]; if(s<V)printf("%d %d %d\n",(V-s)/K,x0,x1); if(s>V)printf("%d %d %d\n",(s-V)/K,x1,x0); } int main(){ scanf("%d%d%d",&N,&K,&V); for(int i=1;i<=N;i++)scanf("%d",&a[i]); Solve(); return 0; }
E - Connected Components?
/* 题目大意:给出一张图,求补图的连通块和其大小 题解:搜索的理论复杂度O(n^2),显然不行, bfs的时候对于已经归入其余连通块的点用并查集进行段无效信息处理,减少搜索树的分支, 显然经过这样的处理搜索分支的数量下降得非常快,就能顺利解决此题了 */ #include <cstdio> #include <algorithm> #include <queue> using namespace std; const int N=200010; int f[N],n,m,vis[N],ans,cnt[N]; vector<int> v[N]; int sf(int x){return f[x]==x?x:f[x]=sf(f[x]);} void bfs(int st){ queue<int> q; q.push(st); cnt[++ans]=1; while(q.size()){ int x=q.front();q.pop();f[x]=sf(x+1); for(int i=0;i<v[x].size();i++)vis[v[x][i]]=x; for(int i=sf(1);i<=n;i=sf(i+1))if(vis[i]!=x){ cnt[ans]++; f[i]=sf(i+1); q.push(i); } } } int main(){ while(~scanf("%d%d",&n,&m)){ ans=0; while(m--){ int x,y; scanf("%d%d",&x,&y); v[x].push_back(y); v[y].push_back(x); } for(int i=1;i<=n+1;i++)f[i]=i; for(int i=1;i<=n;i=sf(i+1))bfs(i); printf("%d\n",ans); sort(cnt+1,cnt+ans+1); for(int i=1;i<=ans;i++)printf("%d%c",cnt[i],i==ans?'\n':' '); }return 0; }
F - SUM and REPLACE
/* 题目大意:给出一个序列,要求维护区间求和,和区间修改操作, 修改操作是将区间中每个数变为其约数个数 题解:我们计算一下约数个数发现,D(1)=1,D(2)=2,如果一个区间全是这样的数字的话 我们就不需要对这个区间进行修改,所以我们只要暴力改数值,当发现一个区间的最大值是1或2时 我们就停止往下修改 */ #include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int N=1000010,M=2000010; int p[N]; namespace Segment_Tree{ int tot; struct node{int l,r,a,b;LL mx,val;}T[M]; void build(int,int); void Initialize(int n){ tot=0; build(1,n); } void up(int x){ T[x].val=T[T[x].l].val+T[T[x].r].val; T[x].mx=max(T[T[x].l].mx,T[T[x].r].mx); } void build(int l,int r){ int x=++tot; T[x].a=l;T[x].b=r;T[x].l=T[x].r=T[x].val=T[x].mx=0; if(l==r){scanf("%lld",&T[x].val);T[x].mx=T[x].val;return;} int mid=(l+r)>>1; T[x].l=tot+1;build(l,mid); T[x].r=tot+1;build(mid+1,r); up(x); } void change(int x,int a,int b){ if(T[x].mx==1||T[x].mx==2)return; if(T[x].a==T[x].b){T[x].mx=T[x].val=p[T[x].val];return;} int mid=(T[x].a+T[x].b)>>1; if(mid>=a&&T[x].l)change(T[x].l,a,b); if(mid<b&&T[x].r)change(T[x].r,a,b); up(x); } LL query(int x,int a,int b){ if(T[x].a>=a&&T[x].b<=b)return T[x].val; int mid=(T[x].a+T[x].b)>>1; LL res=0; if(mid>=a&&T[x].l)res+=query(T[x].l,a,b); if(mid<b&&T[x].r)res+=query(T[x].r,a,b); return res; } } int n,m,t,l,r; int main(){ for(int i=1;i<=1000000;i++){ for(int j=i;j<=1000000;j+=i)p[j]++; } scanf("%d%d",&n,&m); Segment_Tree::Initialize(n); while(m--){ scanf("%d%d%d",&t,&l,&r); if(t==1)Segment_Tree::change(1,l,r); else printf("%lld\n",Segment_Tree::query(1,l,r)); }return 0; }
G - List Of Integers
/* 题目大意:求出比x大的和p互质的第k大的数 题解:我们二分答案,将题目转化为判定区间内和一个数互质的数是否大于k 求区间内和一个数互质的数的个数我们可以用容斥来解决,将这个数的约数筛选出来, 然后容斥区间内约数个数出现的次数即可 */ #include <cstdio> #include <algorithm> using namespace std; const int N=10000010; typedef long long LL; #define pw(x) (1LL<<x) int T; LL x,y,k,p; LL cal(LL x,vector<LL> &p){ LL res=0; int n=p.size(); for(int i=0;i<pw(n);i++){ LL t=1,f=1; for(int j=0;j<n;j++)if(i>>j&1)t*=p[j],f*=-1; res+=f*(x/t); }return res; } int main(){ scanf("%d",&T); while(T--){ scanf("%lld%lld%lld",&x,&p,&k); vector<LL> f; for(LL i=2;i*i<=p;i++)if(p%i==0){ f.push_back(i); while(p%i==0)p/=i; } if(p>1)f.push_back(p); k+=cal(x,f); LL l=1LL,r=1000000000000LL,ans; while(l<=r){ LL mid=(l+r)/2; if(cal(mid,f)>=k)ans=mid,r=mid-1; else l=mid+1; }printf("%lld\n",ans); }return 0; }
愿你出走半生,归来仍是少年