[Educational Codeforces Round#22]
来自FallDream的博客,未经允许,请勿转载,谢谢。
晚上去clj博客逛来逛去很开心,突然同学提醒了一下,发现cf已经开始40分钟了,慌的一B,从B题开始写,写完了B到E最后收掉了A,结果太着急B题忘记写一个等号挂掉了。。。。D题瞎dp也挂了很难受。F题还剩5分钟的时候想出来了,如果没迟应该能写完。
A.
你要做n道题 每道题要花费时间ti,有m个可以提交的时间段,你在同一时刻可以交很多代码并且不耗时间,求最早什么时候可以把代码全部交完。
发现只要管最后一道题啥时候交就行了,扫一遍区间。
#include<iostream> #include<cstdio> #define MN 1000 #define INF 1000000000 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,mn=INF,tot=0; int main() { n=read(); for(int i=1;i<=n;++i) tot+=read(); m=read(); for(int i=1;i<=m;++i) { int l=read(),r=read(); if(r>=tot) mn=min(mn,max(0,l-tot)); } printf("%d",mn==INF?-1:mn+tot); return 0; }
B.给定一个区间[l,r]和x,y,定义特殊数字是可以被表示成x^a+y^b(a,b是非负整数)的数,求[l,r]中最长的连续的一段数字,满足这段数字区间内全是非特殊的数字。
l,r<=10^18 2<=x,y<=10^19
显然x^a和y^b只有log种,都拿出来排个序就行了。
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long #define MN (ll)1e18 using namespace std; ll s[10005],s2[10005],mx=0,a[10005];int top1=0,top2=0,top=0; int main() { ll x,y,l,r; scanf("%lld%lld%lld%lld",&x,&y,&l,&r); s[++top1]=1;s2[++top2]=1; for(ll i=x;;i*=x) { s[++top1]=i; if(i>MN/x)break; } for(ll i=y;;i*=y) { s2[++top2]=i; if(i>MN/y)break; } for(int i=1;i<=top1;++i) for(int j=1;j<=top2;++j) if(s[i]+s2[j]>=l&&s[i]+s2[j]<=r) a[++top]=s[i]+s2[j]; if(!top) return 0*printf("%lld",r-l+1); sort(a+1,a+top+1); mx=max(mx,a[1]-l); mx=max(mx,r-a[top]); for(int i=2;i<=top;++i) mx=max(mx,a[i]-a[i-1]-1); cout<<mx; return 0; }
C.
给定一棵树,一个人在x(x!=1)号点,另一个在1号点,每次行动每个人可以移动到一个相邻节点或者不动,第一个人希望回合最多,第二个人希望最少,求最多有多少个回合。
n<=2*10^5
枚举目标点,一个点可行当且仅当x到它和x的lca的距离小于1号点到lca的距离。
#include<iostream> #include<cstdio> #include<algorithm> #define MD 18 #define MN 200000 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; } pair<int,int> p[MN+5]; int cnt=0,head[MN+5],fa[MD+5][MN+5],dep[MN+5],f[MN+5],n; struct edge{int to,next;}e[MN*2+5]; inline void ins(int f,int t) { e[++cnt]=(edge){t,head[f]};head[f]=cnt; e[++cnt]=(edge){f,head[t]};head[t]=cnt; } bool cmp(pair<int,int> x,pair<int,int> y){return x.first>y.first;} void Dfs(int x,int f) { fa[0][x]=f;p[x]=make_pair(dep[x],x); for(int i=head[x];i;i=e[i].next) if(e[i].to!=f) dep[e[i].to]=dep[x]+1,Dfs(e[i].to,x); } inline int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int k=dep[x]-dep[y],j=0;k;k>>=1,++j) if(k&1)x=fa[j][x]; if(x==y) return x; for(int i=MD;~i;--i) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y]; return fa[0][x]; } int main() { n=read();int x=read(); for(int i=1;i<n;++i) ins(read(),read()); Dfs(1,0); for(int i=1;i<=MD;++i) for(int j=1;j<=n;++j) fa[i][j]=fa[i-1][fa[i-1][j]]; sort(p+1,p+n+1,cmp); for(int i=1;i<=n;++i) { int z=lca(x,p[i].second); if(dep[x]-dep[z]>=dep[z]) continue; return 0*printf("%d\n",p[i].first*2); } return 0; }
D.
给定一个长度为n(<=5000)的序列ai,定义优秀的序列是满足相邻元素相差1或者在膜7意义下相等的序列,求这个序列的两个不相交的优秀子序列满足长度之和最大。
f[i][j]表示两个序列结尾分别在i,j的最长长度
枚举i,j,对于每个i,开两个数组分别表示最后一个数是k和最后一个数膜7等于k的最长长度,然后分别转移。
#include<iostream> #include<cstdio> #include<cstring> #define MN 5000 using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } int n,a[MN+5],f[MN+5],F[8],D[MN+5][MN+5],ans=0,ha[100005],cnt=0; inline void R(int&x,int y){y>x?x=y:0;} int main() { n=read(); for(int i=1;i<=n;++i)!ha[a[i]=read()]?ha[a[i]]=++cnt:0; for(int i=0;i<=n;++i) { memset(F,0,sizeof(F)); memset(f,0,sizeof(f)); int Mx=D[i][0]; for(int j=1;j<i;++j) R(F[a[j]%7],D[i][j]), R(f[ha[a[j]]],D[i][j]); for(int j=i+1;j<=n;++j) { R(D[i][j],Mx+1); R(D[i][j],max(f[ha[a[j]]],F[a[j]%7])+1); if(ha[a[j]-1]) R(D[i][j],f[ha[a[j]-1]]+1); if(ha[a[j]+1]) R(D[i][j],f[ha[a[j]+1]]+1); R(F[a[j]%7],D[i][j]);R(f[ha[a[j]]],D[i][j]); R(D[j][i],D[i][j]);R(ans,D[i][j]); } } cout<<ans; return 0; }
E. Army Creation
给定一个长度为n(<=100000)的序列ai,每次询问一段区间,满足每个数字出现次数不超过k(预先给定)次的情况下最多能保留几个数字。
从前往后扫一遍,找到每个数字前面的第k个相同的数字的位置pos,右区间>=i且左区间<=pos的时候答案减1,可持久化线段树维护即可。
复杂度nlogn
#include<iostream> #include<cstdio> #include<vector> #define MN 100000 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; } vector<int> v[MN+5]; int n,k,num[MN+5],rt[MN+5],cnt=0,last=0,a[MN+5]; struct Tree{int l,r,x;}T[5000000]; void Modify(int x,int nx,int k) { int l=1,r=n,mid;T[nx].x=T[x].x+1; while(l<r) { mid=l+r>>1; if(k<=mid)T[nx].r=T[x].r,T[nx].l=++cnt,x=T[x].l,nx=T[nx].l,r=mid; else T[nx].l=T[x].l,T[nx].r=++cnt,x=T[x].r,nx=T[nx].r,l=mid+1; T[nx].x=T[x].x+1; } } int Query(int x,int k) { int sum=0,l=1,r=n,mid; while(l<r) { mid=l+r>>1; if(k<=mid) sum+=T[T[x].r].x,x=T[x].l,r=mid; else x=T[x].r,l=mid+1; } return sum+T[x].x; } int main() { n=read();k=read(); for(int i=1;i<=n;++i) v[a[i]=read()].push_back(i); for(int i=1;i<=n;++i) if(++num[a[i]]>k) Modify(rt[i-1],rt[i]=++cnt,v[a[i]][num[a[i]]-k-1]); else rt[i]=rt[i-1]; for(int q=read();q;--q) { int l=(read()+last)%n+1,r=(read()+last)%n+1; if(l>r) swap(l,r); printf("%d\n",last=(r-l+1)-Query(rt[r],l)); } return 0; }
F.
给你n个点,支持删边,加边,询问是否是二分图。
n,q<=10^5
考虑线段树分治避免删除操作,然后带权并差集+启发式合并维护答案即可。
复杂度nlog^2n
#include<iostream> #include<cstdio> #include<map> #include<vector> #define pa pair<int,int> #define mp(x,y) make_pair(x,y) #define MN 100000 using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } bool Ans[MN+5]; map<long long,int> mp; int n,q,cnt=1,fa[MN+5],W[MN+5],val[MN+5],size[MN+5],A[MN+5],B[MN+5]; struct Tree{int l,r;vector<pa> x;vector<int> v;}T[MN*4+5]; void Ins(int x,int l,int r,pa v) { if(T[x].l==l&&T[x].r==r) {T[x].x.push_back(v);return;} int mid=T[x].l+T[x].r>>1; if(r<=mid) Ins(x<<1,l,r,v); else if(l>mid) Ins(x<<1|1,l,r,v); else Ins(x<<1,l,mid,v),Ins(x<<1|1,mid+1,r,v); } void build(int x,int l,int r) { if((T[x].l=l)==(T[x].r=r))return; int mid=l+r>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); } inline int getfa(int x) { if(fa[x]==x) return x; int F=getfa(fa[x]); return val[x]=val[fa[x]]^W[x],F; } void Solve(int k,bool flag) { for(int i=0;i<T[k].x.size();++i) { int x=T[k].x[i].first,y=T[k].x[i].second; int f1=getfa(x),f2=getfa(y); if(size[f1]>size[f2]) swap(f1,f2); if(f1==f2) flag|=(val[x]==val[y]); else { size[f2]+=size[f1];fa[f1]=f2; W[f1]=val[x]==val[y]; T[k].v.push_back(f1); } } if(T[k].l==T[k].r) Ans[T[k].l]=flag; else Solve(k<<1,flag),Solve(k<<1|1,flag); for(int i=0;i<T[k].v.size();++i) { int x=T[k].v[i];fa[x]=x; size[fa[x]]-=size[x];W[x]=val[x]=0; } } int main() { n=read();q=read();build(1,1,q); for(int i=1;i<=n;++i) fa[i]=i,size[i]=1,val[i]=W[i]=0; for(int i=1;i<=q;++i) { int x=read(),y=read(); if(mp[1LL*x*MN+y]) { Ins(1,mp[1LL*x*MN+y],i-1,mp(x,y)); mp[1LL*x*MN+y]=0; } else mp[1LL*x*MN+y]=i; A[i]=x;B[i]=y; } for(int i=1;i<=q;++i) { int x=mp[1LL*A[i]*MN+B[i]]; if(x) mp[1LL*A[i]*MN+B[i]]=0,Ins(1,x,q,mp(A[i],B[i])); } Solve(1,0); for(int i=1;i<=q;++i) puts(Ans[i]?"NO":"YES"); return 0; }