Codeforces赛记
(持续更新)
Hello 2020
A:
为什么不是天干地支
#include<cstdio> #define For(i,A,B) for(i=A;i<=(B);++i) char s[25][15],t[25][15]; int main(){ int n,m,q,x,i; scanf("%d%d",&n,&m); For(i,0,n-1)scanf("%s",s[i]); For(i,0,m-1)scanf("%s",t[i]); scanf("%d",&q); while(q--){ scanf("%d",&x); printf("%s%s\n",s[(x-1)%n],t[(x-1)%m]); } return 0; }
B:
看一下数列是不是降序的就好啦
#include<cstdio> #include<algorithm> #define For(i,A,B) for(i=A;i<=(B);++i) using namespace std; const int N=100050; int a[N],b[N],t[N]; int main(){ int n,l,i,j,cnt=0; long long ans=0ll; bool ok; scanf("%d",&n); t[0]=1000001; For(i,1,n){ scanf("%d",&l); ok=0; For(j,1,l){ scanf("%d",t+j); if(t[j]>t[j-1])ok=1; } if(ok)ans+=n; else{a[++cnt]=t[1];b[cnt]=t[l];} } if(cnt){ sort(b+1,b+cnt+1); For(i,1,cnt)ans+=n-cnt+(lower_bound(b+1,b+cnt+1,a[i])-b-1); } printf("%I64d",ans); return 0; }
C:
排列计数
#include<cstdio> #define For(i,A,B) for(i=A;i<=(B);++i) int f[250050]; int main(){ int n,mod,i,ans=0; scanf("%d%d",&n,&mod); f[0]=f[1]=1; For(i,2,n)f[i]=1ll*f[i-1]*i%mod; For(i,1,n)ans=(ans+1ll*(n-i+1)*(n-i+1)%mod*f[i]%mod*f[n-i])%mod; printf("%d",ans); return 0; }
D:
可以发现原要求等价于A,B中的冲突关系完全相同,可以主席树硬搞过去。
#include<cstdio> #include<cstring> #include<algorithm> #define For(i,A,B) for(i=A;i<=(B);++i) #define Ford(i,B,A) for(i=B;i>=(A);--i) using namespace std; const int N=100050; struct node{ int sa,ea,sb,eb; }a[N]; int t[N<<1],rt1[N<<1],rt2[N<<1],lc[N*80],rc[N*80],tot,n,m1,m2; bool cnt[N*80],addv[N*80]; inline bool cmp1(const node &a,const node &b){return a.ea<b.ea;} inline bool cmp2(const node &a,const node &b){return a.sa<b.sa;} int add(int pre,int L,int R,int x,int y){ if(addv[pre])return pre; int o=++tot; cnt[o]=1; if(x<=L&&y>=R){addv[o]=1;lc[o]=lc[pre];rc[o]=rc[pre];} else{ int M=L+R>>1; addv[o]=0; if(x<=M)lc[o]=add(lc[pre],L,M,x,y); else lc[o]=lc[pre]; if(y>M)rc[o]=add(rc[pre],M+1,R,x,y); else rc[o]=rc[pre]; } return o; } bool ask(int o,int L,int R,int x,int y){ if(addv[o])return 0; if(x<=L&&y>=R)return !cnt[o]; int M=L+R>>1; return (x>M||ask(lc[o],L,M,x,y))&&(y<=M||ask(rc[o],M+1,R,x,y)); } inline bool check(){ int i,k=1; memset(rt1,0,sizeof(rt1));memset(rt2,0,sizeof(rt2));tot=0; sort(a+1,a+n+1,cmp1); For(i,1,m1){ rt1[i]=rt1[i-1]; for(;k<=n&&a[k].ea==i;++k)rt1[i]=add(rt1[i],1,m2,a[k].sb,a[k].eb); } sort(a+1,a+n+1,cmp2);k=n; Ford(i,m1,1){ rt2[i]=rt2[i+1]; for(;k&&a[k].sa==i;--k)rt2[i]=add(rt2[i],1,m2,a[k].sb,a[k].eb); } For(i,1,n)if(!ask(rt1[a[i].sa-1],1,m2,a[i].sb,a[i].eb)||!ask(rt2[a[i].ea+1],1,m2,a[i].sb,a[i].eb)){ puts("NO"); return 0; } return 1; } int main(){ int i; scanf("%d",&n); For(i,1,n){ scanf("%d%d%d%d",&a[i].sa,&a[i].ea,&a[i].sb,&a[i].eb); t[(i<<1)-1]=a[i].sa;t[i<<1]=a[i].ea; } sort(t+1,t+(n<<1)+1);m1=unique(t+1,t+(n<<1)+1)-t-1; For(i,1,n){ a[i].sa=lower_bound(t+1,t+m1+1,a[i].sa)-t; a[i].ea=lower_bound(t+1,t+m1+1,a[i].ea)-t; } For(i,1,n){t[(i<<1)-1]=a[i].sb;t[i<<1]=a[i].eb;} sort(t+1,t+(n<<1)+1);m2=unique(t+1,t+(n<<1)+1)-t-1; For(i,1,n){ a[i].sb=lower_bound(t+1,t+m2+1,a[i].sb)-t; a[i].eb=lower_bound(t+1,t+m2+1,a[i].eb)-t; } if(!check())return 0; For(i,1,n){swap(a[i].sa,a[i].sb);swap(a[i].ea,a[i].eb);} swap(m1,m2); if(check())puts("YES"); return 0; }
E:
考虑所有五点集合,把其中凸包为三角形、四边形、五边形的集合数记为$c_3,c_4,c_5$,则$c_3+c_4+c_5=C^5_n$。
由于任意三点不共线,易知答案为$2c_3+c_4$。
接下来考虑$3c_3+4c_4+5c_5$,它应该等于各个凸包的边数和,设固定i到j的向量后,在该向量左侧的点数为$tot_{i,j}$,则总边数为$\sum\limits^n_{i=1}\sum\limits_{1\le j\le n,j\not=i}C^3_{tot_{i,j}}$,这个可以固定i以后用极角序+双指针求出来,复杂度$O(n^2logn)$。
#include<cstdio> #include<algorithm> #include<utility> #define For(i,A,B) for(i=(A);i<=(B);++i) #define fi first #define se second using namespace std; typedef long long ll; const int N=2505; inline bool Crs(const pair<int,int> &a,const pair<int,int> &b){return (ll)a.fi*b.se-(ll)a.se*b.fi>=0ll;} inline bool cmp(const pair<int,int> &a,const pair<int,int> &b){ bool b1=a.fi>0,b2=b.fi>0; if(b1^b2)return b1; return Crs(a,b); } inline ll C3(int n){return (ll)n*(n-1)*(n-2)/6;} int x[N],y[N]; pair<int,int> t[N]; int main(){ int n,i,j,k,cnt; ll ans; scanf("%d",&n); ans=(ll)n*(n-1)*(n-2)*(n-3)/24*(n-4); For(i,1,n)scanf("%d%d",x+i,y+i); For(i,1,n){ cnt=0; For(j,1,n)if(i!=j)t[++cnt]=make_pair(x[j]-x[i],y[j]-y[i]); sort(t+1,t+n,cmp); k=1; For(j,1,n-1){ while(k<j+n-1&&Crs(t[j],t[(k-1)%(n-1)+1]))++k; ans-=C3(k-j-1); } } printf("%I64d",ans); return 0; }
Round #609(Div 1)
A:
能保留前k位则保留,否则要在第k位上+1
#include<cstdio> #define For(i,A,B) for(i=A;i<=(B);++i) int a[200050]; int main(){ int n,k,i; bool ok=0; scanf("%d%d",&n,&k); For(i,1,n)scanf("%1d",a+i); For(i,k+1,n)if(a[i]!=a[i-k]){ok=0;break;} if(!ok){ ok=1; For(i,k+1,n)if(a[i]>a[(i-1)%k+1]){ok=0;break;} else if(a[i]<a[(i-1)%k+1])break; if(!ok){ ++a[i=k]; for(;a[i]==10;--i){a[i]=0;++a[i-1];} } For(i,k+1,n)a[i]=a[i-k]; } printf("%d\n",n); For(i,1,n)printf("%d",a[i]); return 0; }
B:
只有奇数高度的地方会出现空缺,两两配对把空缺填满。
#include<cstdio> #define For(i,A,B) for(i=A;i<=(B);++i) typedef long long ll; int a[300050],c[2]; int main(){ int n,i; ll ans=0ll; scanf("%d",&n); For(i,1,n)scanf("%d",a+i); For(i,1,n)ans+=a[i]>>1; For(i,1,n)if(a[i]&1)if(c[(i&1)^1]){++ans;--c[(i&1)^1];} else ++c[i&1]; printf("%I64d",ans); return 0; }
Round #607(DIv 1)
A:
只需要保留前x个就可以了。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define rep(i,A,B) for(i=A;i<(B);++i) using namespace std; const int mod=1000000007; char s[505]; int x,p,len; vector<char> st,tmp; inline void work(){ tmp.clear(); for(int i=p+1;i<st.size()&&st.size()+tmp.size()<x;++i)tmp.push_back(st[i]); } inline void cpy(){ for(int i=0;st.size()<x&&i<tmp.size();++i)st.push_back(tmp[i]); } int main(){ int T,i; scanf("%d",&T); while(T--){ scanf("%d%s",&x,s);len=strlen(s); st.clear(); rep(i,0,min(len,x))st.push_back(s[i]); rep(p,0,x)if(st[p]=='2'){ work(); len=(len*2ll-p-1+mod)%mod; cpy(); }else if(st[p]=='3'){ work(); len=(len*3ll+(mod-p-1<<1))%mod; cpy();cpy(); } printf("%d\n",len); } return 0; }
B:
分类讨论,注意可能全是好人
#include<cstdio> #define For(i,A,B) for(i=A;i<=(B);++i) char a[65][65]; int s[65][65]; inline int sum(int x1,int y1,int x2,int y2){return s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];} int main(){ int T,n,m,i,j; bool ok; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); For(i,1,n){ scanf("%s",a[i]+1); For(j,1,m)s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(a[i][j]=='P'); } if(!s[n][m])puts("0"); else if(s[n][m]==n*m)puts("MORTAL"); else if(!sum(1,1,1,m)||!sum(1,1,n,1)||!sum(n,1,n,m)||!sum(1,m,n,m))puts("1"); else if(a[1][1]=='A'||a[1][m]=='A'||a[n][1]=='A'||a[n][m]=='A')puts("2"); else{ ok=0; For(i,2,n-1)if(!sum(i,1,i,m)){ok=1;break;} if(ok){puts("2");continue;} For(i,2,m-1)if(!sum(1,i,n,i)){ok=1;break;} if(ok)puts("2"); else puts(sum(1,1,1,m)<m||sum(1,1,n,1)<n||sum(n,1,n,m)<m||sum(1,m,n,m)<n?"3":"4"); } } return 0; }
C:
结论题?
#include<cstdio> #include<cstring> #include<algorithm> #define For(i,A,B) for(i=A;i<=(B);++i) #define Go(u) for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa) using namespace std; typedef long long ll; const int N=200050; int G[N],to[N<<1],w[N<<1],nxt[N<<1],sz,siz[N],n; ll ans1,ans2; inline void adde(int u,int v,int c){ to[++sz]=v;w[sz]=c;nxt[sz]=G[u];G[u]=sz; to[++sz]=u;w[sz]=c;nxt[sz]=G[v];G[v]=sz; } bool dfs(int u,int fa){ int i,v; siz[u]=1; Go(u){ dfs(v,u); if(siz[v]&1)ans1+=w[i]; ans2+=(ll)w[i]*min(siz[v],n-siz[v]); siz[u]+=siz[v]; } } int main(){ int T,i,u,v,c; scanf("%d",&T); while(T--){ scanf("%d",&n);n<<=1; memset(G,0,sizeof(G));sz=0; For(i,2,n){ scanf("%d%d%d",&u,&v,&c); adde(u,v,c); } ans1=ans2=0ll; dfs(1,0); printf("%I64d %I64d\n",ans1,ans2); } return 0; }
Round #606(Div 1)
A:
一个简单的贪心:出现twone时删掉o,否则one删掉n,two删掉w。
#include<cstdio> #include<cstring> #define For(i,A,B) for(i=A;i<=(B);++i) char s[150050]; short a[150050]; int p[150050]; int main(){ #ifdef DEBUG freopen("a.in","r",stdin); #endif int T,n,i,ans; scanf("%d",&T); while(T--){ scanf("%s",s+1);n=strlen(s+1); For(i,1,n-2)if(s[i]=='o'&&s[i+1]=='n'&&s[i+2]=='e')a[i]=1; else if(s[i]=='t'&&s[i+1]=='w'&&s[i+2]=='o')a[i]=2; else a[i]=0; ans=0; For(i,1,n-2)if(i<=n-4&&a[i]==2&&a[i+2]==1){ p[++ans]=i+2; i+=4; }else if(a[i])p[++ans]=i+1; printf("%d\n",ans); For(i,1,ans)printf("%d ",p[i]); putchar('\n'); } return 0; }
B:
删掉B,从A出发,记走不到的点数(不包括B)为$res_A$,那么答案就是$res_A\times res_B$。
#include<cstdio> #include<cstring> #include<queue> #define For(i,A,B) for(i=A;i<=(B);++i) #define Go(u) for(i=G[u];i;i=nxt[i])if(!vis[v=to[i]]&&v!=ban) using namespace std; const int N=200050; const int M=1000050; int G[N],to[M],nxt[M],sz; bool vis[N],f[2][N]; queue<int> Q; inline void adde(int u,int v){ to[++sz]=v;nxt[sz]=G[u];G[u]=sz; to[++sz]=u;nxt[sz]=G[v];G[v]=sz; } inline void bfs(int u,int ban,bool p){ int i,v; memset(vis,0,sizeof(vis)); vis[u]=1;Q.push(u); while(!Q.empty()){ u=Q.front();Q.pop(); Go(u){ f[p][v]=1; if(p&&f[0][v])f[0][v]=f[1][v]=0; vis[v]=1; Q.push(v); } } } int main(){ int T,n,m,a,b,i,u,v,cnt1,cnt2; scanf("%d",&T); while(T--){ scanf("%d%d%d%d",&n,&m,&a,&b); memset(G,0,sizeof(G));sz=0; For(i,1,m){ scanf("%d%d",&u,&v); adde(u,v); } memset(f,0,sizeof(f)); bfs(a,b,0);f[0][a]=0; bfs(b,a,1);f[1][b]=0; cnt1=cnt2=0; For(i,1,n){ if(f[0][i])++cnt1; if(f[1][i])++cnt2; } printf("%I64d\n",1ll*cnt1*cnt2); } return 0; }
C:
排序+前缀和乱搞。
#include<cstdio> #include<algorithm> #include<vector> #define For(i,A,B) for(i=A;i<=(B);++i) using namespace std; const int N=400050; struct node{ int v,c; }p[N]; int a[N],b[N],s[N],used[N],t[N]; vector<int> ans[650]; inline bool cmp(const node &a,const node &b){return a.c<b.c;} int main(){ int n,m,i,j,x,y,k,res=0,sx,sy; scanf("%d",&n); For(i,1,n){ scanf("%d",a+i); b[i]=a[i]; } sort(b+1,b+n+1);m=unique(b+1,b+n+1)-b-1; For(i,1,m){p[i].v=b[i];p[i].c=0;} For(i,1,n)++p[lower_bound(b+1,b+m+1,a[i])-b].c; sort(p+1,p+m+1,cmp); For(i,1,m)s[i]=s[i-1]+p[i].c; k=m+1; for(x=m;x;--x){ while(p[k-1].c>x)--k; y=s[k-1]/x+m+1-k; if(x<=y&&x*y>res)res=(sx=x)*(sy=y); } printf("%d\n%d %d\n",res,sx,sy); k=m; For(i,1,sy) For(j,1,sx){ if(used[k]==p[k].c||used[k]==sx)--k; ans[j].push_back(p[k].v); ++used[k]; } For(i,1,sx){ For(j,0,sy-1)t[(i+j)%sy]=ans[i][j]; For(j,0,sy-1)printf("%d ",t[j]); putchar('\n'); } return 0; }
Round #596(Div 1)
也不知是什么给了我勇气打Div 1
C:
可以发现i个数相加的结果是$2^{a_1}+2^{a_2}+\cdots+2^{a_i}+ip$,所以不断给n加p,然后判断n的二进制展开有几个1就行了,顶多枚举$O(logn)$次。
#include<cstdio> inline int cnt(int x){ int s=0; for(;x;x^=x&-x)++s; return s; } int main(){ int n,p,i; scanf("%d%d",&n,&p); for(i=1,n-=p;n>=i;++i,n-=p)if(cnt(n)<=i){ printf("%d",i); return 0; } puts("-1"); return 0; }
D:
把每个数质因数分解,可以发现幂指数的非零位顶多$O(loga_i)$个,并且要使它乘上一个数后是$x^k$,那个数的幂指数和它相加后必须全是k的倍数,因此把指数模k,维护即可。
(注意用multiset会被卡到$O(n^2)$,map/Treap$O(nlog^2n)$于是我傻乎乎地手写了Treap)
#include<cstdio> #include<cstdlib> #include<ctime> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int N=100050; bool f[N]; int a[N],p[N],pcnt=0,k,cnt[N],r[N],ch[N][2],sz=0,rt=0; struct node{ vector<int> id,cnt; node(){} node(int x){ for(int i=1,t;p[i]*p[i]<=x;++i)if(!(x%p[i])){ for(t=0;!(x%p[i]);x/=p[i])if(++t>=k)t-=k; if(t){ id.push_back(i); cnt.push_back(t); } } if(x>1){ id.push_back(lower_bound(p+1,p+pcnt+1,x)-p); cnt.push_back(1); } } inline bool operator <(const node &b)const{ if(id.size()!=b.id.size())return id.size()<b.id.size(); for(int i=0;i<id.size();++i)if(id[i]!=b.id[i])return id[i]<b.id[i]; else if(cnt[i]!=b.cnt[i])return cnt[i]<b.cnt[i]; return 0; } inline bool operator >(const node &b)const{return b<*this;} inline bool operator ==(const node &b)const{ if(id.size()!=b.id.size())return 0; for(int i=0;i<id.size();++i)if(id[i]!=b.id[i])return 0; else if(cnt[i]!=b.cnt[i])return 0; return 1; } inline void clear(){id.clear();cnt.clear();} }tmp,tt,v[N]; inline void build(){ int i,j; for(i=2;i*i<=100000;++i)if(!f[i]) for(j=i*i;j<=100000;j+=i)f[j]=1; for(i=2;i<=100000;++i)if(!f[i])p[++pcnt]=i; } inline int newnode(){ v[++sz]=tmp;cnt[sz]=1;r[sz]=rand(); return sz; } inline void rotate(int &o,short d){ int k=ch[o][d^1]; ch[o][d^1]=ch[k][d];ch[k][d]=o;o=k; } inline short cmp(int o){ if(tmp==v[o])return -1; return tmp>v[o]; } void ins(int &o){ if(!o)o=newnode(); else{ short d=cmp(o); if(d==-1)++cnt[o]; else{ ins(ch[o][d]); if(r[ch[o][d]]>r[o])rotate(o,d^1); } } } int ask(int o){ if(!o)return 0; short d=cmp(o); if(d==-1)return cnt[o]; return ask(ch[o][d]); } int main(){ srand(time(NULL)); int n,i,j; ll ans=0ll; scanf("%d%d",&n,&k); for(i=1;i<=n;++i)scanf("%d",a+i); build(); for(i=1;i<=n;++i){ tt=node(a[i]);tmp.clear(); for(j=0;j<tt.id.size();++j){ tmp.id.push_back(tt.id[j]); tmp.cnt.push_back(k-tt.cnt[j]); } ans+=ask(rt); tmp=tt; ins(rt); } printf("%I64d",ans); return 0; }
其余的写挂了,挖坑待填
Round #585(Div 2)
A:
暴力能过就乱搞吧。
#include<cstdio> inline int Min(int a,int b){return a<b?a:b;} inline int Max(int a,int b){return b<a?a:b;} inline int calc1(int n,int a,int k){return Min(a,Max(0,n-(k-1)*a));} inline int calc2(int n,int a,int k){return Min(a,n/k);} int main(){ int a1,k1,a2,k2,n,i,minn=0x3f3f3f3f,maxn=0; scanf("%d%d%d%d%d",&a1,&a2,&k1,&k2,&n); for(i=0;i<=n;++i)if(i<=a1*k1&&n-i<=a2*k2){ minn=Min(minn,calc1(i,a1,k1)+calc1(n-i,a2,k2)); maxn=Max(maxn,calc2(i,a1,k1)+calc2(n-i,a2,k2)); } printf("%d %d",minn,maxn); return 0; }
B:
最终结果只跟元素的正负性有关,所以记录一下的正负性就可以了。
#include<cstdio> int cnt[2]; int main(){ int n,i,x; long long ans1=0ll,ans2=0ll; bool sum=0; scanf("%d",&n); cnt[0]=1; for(i=0;i<n;++i){ scanf("%d",&x);sum^=(x<0); if(sum){ans1+=cnt[0];ans2+=cnt[1];} else{ans1+=cnt[1];ans2+=cnt[0];} ++cnt[sum]; } printf("%I64d %I64d",ans1,ans2); return 0; }
C:
考虑贪心。首先只要考虑'a'两两配对,那么'b'也就自动排好了。如果'a'总共有奇数个,那么显然无解。
原来就相等的位置的就不用动了,对于原来不等的位置,两两配对交换。需要注意的是如果最后恰好剩下相同的两对,那么要和一对已经换好的交换才能达到合法状态。
#include<cstdio> #include<vector> using namespace std; const int N=200050; char s[N],t[N]; bool b[N]; vector<int> ps,pt,ss,st; struct node{ int x,y; node(){} node(int x,int y):x(x),y(y){} }; vector<node> ans; inline void work(vector<int> &p){ int x,y; while(p.size()>1){ x=p[p.size()-1];y=p[p.size()-2];p.pop_back();p.pop_back(); ans.push_back(node(x,y)); } } int main(){ int n,i; scanf("%d%s%s",&n,s+1,t+1); for(i=1;i<=n;++i){ if(s[i]=='a'){ ps.push_back(i); b[i]=1; } if(t[i]=='a')pt.push_back(i); } if((ps.size()+pt.size())&1){ puts("-1"); return 0; } for(i=0;i<pt.size();++i)if(b[pt[i]])b[pt[i]]=0; else st.push_back(pt[i]); for(i=0;i<ps.size();++i)if(b[ps[i]])ss.push_back(ps[i]); work(ss);work(st); if(ss.size()){ans.push_back(node(ss[0],ss[0]));ans.push_back(node(ss[0],st[0]));} printf("%d\n",ans.size()); for(i=0;i<ans.size();++i)printf("%d %d\n",ans[i].x,ans[i].y); return 0; }
D:
博弈问题。跟简单博弈论问题相似的是,最优方案中一定存在A填一个i,B填一个9-i的策略,进一步思考即可得到答案。
#include<cstdio> #include<cctype> char s[200050]; inline void Swap(int &a,int &b){int t=a;a=b;b=t;} int main(){ int n,i,tot1=0,tot2=0,sum1=0,sum2=0; scanf("%d%s",&n,s+1); for(i=1;i<=(n>>1);++i)if(isdigit(s[i]))sum1+=s[i]&15; else ++tot1; for(;i<=n;++i)if(isdigit(s[i]))sum2+=s[i]&15; else ++tot2; if(tot1!=tot2){ if(tot1>tot2){Swap(tot1,tot2);Swap(sum1,sum2);} if(sum1<=sum2)puts("Monocarp"); else puts(sum1-sum2==(tot2-tot1>>1)*9?"Bicarp":"Monocarp"); }else puts(sum1==sum2?"Bicarp":"Monocarp"); return 0; }
E:
注意到它的颜色数很少,因此可以状压DP,预处理出每步转移的代价即可。
#include<cstdio> #include<cstring> #include<vector> using namespace std; typedef long long ll; ll cnt[25][25],f[1<<20]; vector<int> p[25],tmp; inline void chkmin(ll &a,ll b){if(b<a)a=b;} int main(){ int n,i,j,k,t,x; ll res; scanf("%d",&n); for(i=1;i<=n;++i){ scanf("%d",&x); p[x-1].push_back(i); } for(i=0;i<20;++i)if(p[i].size()) for(j=0;j<20;++j)if(i!=j&&p[j].size()) for(k=t=0;k<p[i].size()&&t<p[j].size();++k){ while(t<p[j].size()-1&&p[j][t+1]<p[i][k])++t; if(p[i][k]>p[j][t])cnt[i][j]+=t+1; } memset(f,0x3f,sizeof(f)); for(k=f[0]=0;k<(1<<20);++k){ tmp.clear(); for(i=0;i<20;++i)if((k>>i)&1)tmp.push_back(i); for(i=0;i<20;++i)if(!((k>>i)&1)){ for(j=res=0;j<tmp.size();++j)res+=cnt[tmp[j]][i]; chkmin(f[k|(1<<i)],f[k]+res); } } printf("%I64d",f[(1<<20)-1]); return 0; }
F:
其它约束都是典型2-SAT,只要考虑一下必须有公共部分怎么弄。
显然当且仅当满足$l_i>r_j$或$r_i<l_j$时选i再j是不合法的,对于两种情况分开处理,比如对于第一种,按照$r_i$排序后可以转化为一次区间连边,用前/后缀和优化建图即可。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; const int N=400050; struct node{ int l,r,id; }a[N]; int G[N<<2],to[N*10],nxt[N*10],sz=0,dfn[N<<2],col[N<<2],dfsc=0,st[N<<2],sl=-1,cnt=0,tot,id[N],n; vector<int> ans; inline bool cmp1(node a,node b){return a.r<b.r;} inline bool cmp2(node a,node b){return a.l<b.l;} inline bool cmp3(node a,node b){return a.id<b.id;} inline void adde(int u,int v){ to[++sz]=v;nxt[sz]=G[u];G[u]=sz; } int dfs(int u){ int i,v,lowu=dfn[u]=++dfsc,lowv; st[++sl]=u; for(i=G[u];i;i=nxt[i])if(!dfn[v=to[i]]){ lowv=dfs(v); if(lowv<lowu)lowu=lowv; }else if(!col[v=to[i]]&&dfn[v]<lowu)lowu=dfn[v]; if(lowu==dfn[u]){ ++cnt; do{ v=st[sl--]; col[v]=cnt; }while(v!=u); } return lowu; } inline int bs1(int x){ int l=0,r=n,mid; while(l<r){ mid=l+r+1>>1; if(a[mid].r<x)l=mid; else r=mid-1; } return l; } inline int bs2(int x){ int l=1,r=n+1,mid; while(l<r){ mid=l+r>>1; if(a[mid].l>x)r=mid; else l=mid+1; } return l; } int main(){ int q,M,m,i,u,v,x,f=0; scanf("%d%d%d%d",&q,&n,&M,&m); while(q--){ scanf("%d%d",&u,&v); adde(u+n,v);adde(v+n,u); } for(i=1;i<=n;++i){scanf("%d%d",&a[i].l,&a[i].r);a[i].id=i;} while(m--){ scanf("%d%d",&u,&v); adde(u,v+n);adde(v,u+n); } tot=n<<1; sort(a+1,a+n+1,cmp1); for(adde(id[1]=++tot,a[1].id+n),i=2;i<=n;++i){adde(id[i]=++tot,id[i-1]);adde(id[i],a[i].id+n);} for(i=1;i<=n;++i)if(x=bs1(a[i].l))adde(a[i].id,id[x]); sort(a+1,a+n+1,cmp2); for(adde(id[n]=++tot,a[n].id+n),i=n-1;i;--i){adde(id[i]=++tot,id[i+1]);adde(id[i],a[i].id+n);} for(i=1;i<=n;++i)if((x=bs2(a[i].r))<=n)adde(a[i].id,id[x]); for(i=1;i<=tot;++i)if(!dfn[i])dfs(i); sort(a+1,a+n+1,cmp3); for(i=1;i<=n;++i){ if(col[i]==col[i+n]){ puts("-1"); return 0; } if(col[i]<col[i+n]){ ans.push_back(i); f=max(f,a[i].l); } } printf("%d %d\n",ans.size(),f); for(i=0;i<ans.size();++i)printf("%d ",ans[i]); return 0; }
Round #578(Div. 2)
A:
签到题。暴力模拟即可。
#include<cstdio> char s[100050]; bool b[15]; int main(){ int n,i,j; scanf("%d%s",&n,s); for(i=0;i<n;++i)if(s[i]=='L')for(j=0;j<=9;++j)if(!b[j]){ b[j]=1; break; }else; else if(s[i]=='R')for(j=9;j>=0;--j)if(!b[j]){ b[j]=1; break; }else; else b[s[i]&15]=0; for(i=0;i<=9;++i)putchar(b[i]?'1':'0'); return 0; }
B:
贪心。因为背包无限大,无疑里面装的方块越多越有利,而$h_i$离开以后不会返回,所以最优方案必定是把它变成$h_{i+1}-k$(如果不行,则无解),然后更新背包,跳到下一堆。
#include<cstdio> int a[105]; int main(){ int T,n,m,k,i,t; bool ok; scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&k); for(i=1;i<=n;++i)scanf("%d",a+i); if(n==1){ puts("YES"); continue; } for(i=1,ok=0;i<n;++i){ t=a[i+1]-k;if(t<0)t=0; if(a[i]+m<t){ puts("NO"); ok=1; break; } m+=a[i]-t; } if(!ok)puts("YES"); } return 0; }
C:
一个很明显的性质是两间房间会被隔开当且仅当它们之间存在上下两堵墙在同一位置,而这种位置有$gcd(n,m)$个,判断一下即可。
#include<cstdio> typedef long long ll; inline ll gcd(ll a,ll b){ ll t; while(b){t=a%b;a=b;b=t;} return a; } inline ll chc(bool b,ll x,ll y){return b?y:x;} int main(){ ll n,m,d,dn,dm,x,y,t1,t2; int q,a,b; scanf("%I64d%I64d%d",&n,&m,&q); dn=n/(d=gcd(n,m));dm=m/d; while(q--){ scanf("%d%I64d%d%I64d",&a,&x,&b,&y); t1=chc(a-1,dn,dm);t2=chc(b-1,dn,dm); puts((x+t1-1)/t1==(y+t2-1)/t2?"YES":"NO"); } return 0; }
D:
首先行和列是相似的,先讨论行,列类似。
首先行在进行一次涂白后可以分为两类,$n-k$行和原来一样,它们之中有几个白行可以前缀和预处理,剩下$k$行被切成了上下两段,会变成白行当且仅当左右两段都是白的,这个可以预处理,枚举涂白的在$[i,i+k)$列,然后用前缀和求出左右两段是否都是白的,对结果再维护一个前缀和就可以$O(1)$查询,总复杂度$O(n^2)$。
#include<cstdio> char s[2005]; bool a[2005][2005]; int sum[2005][2005],res1[2005][2005],res2[2005][2005],w1[2005],w2[2005]; inline int Max(int a,int b){return a>b?a:b;} int main(){ int n,k,i,j,ans=0; scanf("%d%d",&n,&k); for(i=1;i<=n;++i){ scanf("%s",s); for(j=1;j<=n;++j)a[i][j]=s[j-1]=='B'; } for(i=1;i<=n;++i) for(j=1;j<=n;++j)sum[i][j]=sum[i][j-1]+a[i][j]; for(i=1;i<=n-k+1;++i) for(j=1;j<=n;++j)res1[i][j]=res1[i][j-1]+!(sum[j][i-1]||sum[j][n]-sum[j][i+k-1]); for(i=1;i<=n;++i)w1[i]=w1[i-1]+!sum[i][n]; for(j=1;j<=n;++j) for(i=1;i<=n;++i)sum[i][j]=sum[i-1][j]+a[i][j]; for(i=1;i<=n-k+1;++i) for(j=1;j<=n;++j)res2[i][j]=res2[i][j-1]+!(sum[i-1][j]||sum[n][j]-sum[i+k-1][j]); for(i=1;i<=n;++i)w2[i]=w2[i-1]+!sum[n][i]; for(i=1;i<=n-k+1;++i) for(j=1;j<=n-k+1;++j)ans=Max(ans,res1[j][i+k-1]-res1[j][i-1]+res2[i][j+k-1]-res2[i][j-1]+w1[i-1]+w1[n]-w1[i+k-1]+w2[j-1]+w2[n]-w2[j+k-1]); printf("%d",ans); return 0; }
E:
用字符串哈希,枚举到$\min(l_{ans},l_s)$,记录下能匹配的最长长度即可。
然而有神仙卡了自然溢出导致我fst了mdzz
哈希时用大素数+多重哈希保险,由于我面向数据跑单哈希也是能过的。
#include<cstdio> #include<cctype> #include<cstring> typedef unsigned long long ull; const ull mod=1000000000001117; const int base=233; ull p[1000050]; char ans[1000050],s[1000050]; inline int Min(int a,int b){return a<b?a:b;} inline int changer(char c){ if(isdigit(c))return c&15; if(isupper(c))return c-'A'+10; return c-'a'+36; } int main(){ int n,i,j,l,len,lans; ull t1,t2; scanf("%d%s",&n,ans+1);lans=strlen(ans+1); for(i=p[0]=1;i<=1000000;++i)p[i]=p[i-1]*base%mod; for(i=1;i<n;++i){ scanf("%s",s+1);len=strlen(s+1); for(j=1,l=t1=t2=0ull;j<=len;++j){ t1=(t1+changer(ans[lans-j+1])*p[j-1])%mod; t2=(t2*base+changer(s[j]))%mod; if(t1==t2)l=j; } for(j=l+1;j<=len;++j)ans[++lans]=s[j]; } puts(ans+1); return 0; }
F:
比赛的时候做完E只剩12min了就没看出这是道简单题
对付这种边周期性变化的题的常见套路是拆点,对于此题$m_i\le 10$,所以$x$其实只要维护除以$lcm(1,2,\cdots,10)=2520$的余数就可以了,剩下的就是记搜求环大小。
#include<cstdio> #include<cstring> const int N=1005; const int M=2520; int G[N][10],c[N],a[N],f[N][M]; bool vis[N][M],b[N]; int dfs(int u,int k){ k=(k+a[u])%M; if(f[u][k]!=-1)return f[u][k]; if(vis[u][k]){ int v=u,x=k,res=0; do{ v=G[v][x%c[v]];x=(x+a[v])%M; res+=!b[v];b[v]=1; }while(v!=u||x!=k); v=u;x=k; do{ v=G[v][x%c[v]];x=(x+a[v])%M; b[v]=0; }while(v!=u||x!=k); return f[u][k]=res; } vis[u][k]=1; f[u][k]=dfs(G[u][k%c[u]],k); vis[u][k]=0; return f[u][k]; } int main(){ int n,q,i,j,x,y; scanf("%d",&n); for(i=1;i<=n;++i){scanf("%d",a+i);a[i]=(a[i]%M+M)%M;} for(i=1;i<=n;++i){ scanf("%d",c+i); for(j=0;j<c[i];++j)scanf("%d",&G[i][j]); } memset(f,-1,sizeof(f)); scanf("%d",&q); while(q--){ scanf("%d%d",&x,&y); printf("%d\n",dfs(x,(y%M+M)%M)); } return 0; }