B .Aesthetics in poetry
题意:给定N个数,(N<2000 ,a[i] <=1e9),让你找一个最大的K,使得N个数膜K的余数个数全都等于N/K个。
思路:我们找到N的因子,然后验证即可,复杂度O(N^2)
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; int a[maxn],N,ans=-1,num[maxn]; void get(int k) { rep(i,0,k-1) num[i]=0; rep(i,1,N) num[a[i]%k]++; bool F=true; rep(i,0,k-1) if(num[i]!=N/k) F=false; if(!F) return ; if(ans==-1) ans=k; else ans=min(ans,k); } int main() { scanf("%d",&N); rep(i,1,N) scanf("%d",&a[i]); for(int i=2;i*i<=N;i++){ if(N%i==0) { get(i); if(i*i!=N) get(N/i); } } get(N); printf("%d\n",ans); return 0; }View Code
D .Maximizing Advertising
题意:给定二维平面上N个点,其中一些是w,一些是b,让你用两个不相交的矩阵把他们圈起来,一个统计w的个数,一个统计b的个数,使得和最大。
思路:其实就是用一根线把平面分成两个。 枚举分割线即可。
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+5; struct node { int x,y,tp; }a[maxn]; int n,X[maxn],Y[maxn],vis1[maxn],vis2[maxn],sum1,sum2; int gao(int tp,int m) { memset(vis1,0,sizeof(vis1)); memset(vis2,0,sizeof(vis2)); if(tp==1) { for(int i=1;i<=n;i++) if(a[i].tp==1)vis1[a[i].x]++; else vis2[a[i].x]++; } else { for(int i=1;i<=n;i++) if(a[i].tp==1)vis1[a[i].y]++; else vis2[a[i].y]++; } int ans=0,res1=0,res2=0; for(int i=1;i<=m;i++) { res1+=vis1[i],res2+=vis2[i]; ans=max(ans,res1+sum2-res2); ans=max(ans,res2+sum1-res1); } return ans; } int main() { char c[2]; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d%s",&X[i],&Y[i],c); if(c[0]=='b')a[i].tp=1,sum1++; else a[i].tp=2,sum2++; a[i].x=X[i],a[i].y=Y[i]; } sort(X+1,X+1+n); int sz1=unique(X+1,X+1+n)-X-1; sort(Y+1,Y+1+n); int sz2=unique(Y+1,Y+1+n)-Y-1; for(int i=1;i<=n;i++) { a[i].x=lower_bound(X+1,X+1+sz1,a[i].x)-X; a[i].y=lower_bound(Y+1,Y+1+sz2,a[i].y)-Y; } int ans=gao(1,sz1); ans=max(ans,gao(2,sz2)); printf("%d\n",ans); }View Code
E .Group work
题意:给定N个人,问有多少种分组情况,满足这个组至少有两人。
思路:即C(N,2)+C(N,3)+..C(N,N); 直接暴力即可, 也可以用2^N-C(N,1)-C(N,0);
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=110; ll C[maxn][maxn],ans; int main() { rep(i,0,40) C[i][i]=C[i][0]=1; rep(i,1,40) rep(j,1,i) C[i][j]=C[i-1][j]+C[i-1][j-1]; int N; scanf("%d",&N); rep(i,2,N) ans+=C[N][i]; printf("%lld\n",ans); return 0; }View Code
G .Running a penitentiary
题意:给定N个区间[Li,Ri],然后Q次询问,每次给出a,b,问a到b这几个区间的公共长度是多少。
思路:开始还以为要主席树找区间的公共部分,但是发现既然是a到b的区间都有,那么公共部分因为的[b的最大值,a的最小值]。所以离散一下,线段树即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; const int inf=1000000000; int Mx[maxn],Mn[maxn]; void update1(int Now,int L,int R,int pos,int val) { if(L==R) { Mn[Now]=val; return ;} int Mid=(L+R)>>1; if(pos<=Mid) update1(Now<<1,L,Mid,pos,val); else update1(Now<<1|1,Mid+1,R,pos,val); Mn[Now]=min(Mn[Now<<1],Mn[Now<<1|1]); } int query1(int Now,int L,int R,int l,int r) { if(l<=L&&r>=R) return Mn[Now]; int Mid=(L+R)>>1,res=inf; if(l<=Mid) res=min(res,query1(Now<<1,L,Mid,l,r)); if(r>Mid) res=min(res,query1(Now<<1|1,Mid+1,R,l,r)); return res; } void update2(int Now,int L,int R,int pos,int val) { if(L==R){ Mx[Now]=val;return ;} int Mid=(L+R)>>1; if(pos<=Mid) update2(Now<<1,L,Mid,pos,val); else update2(Now<<1|1,Mid+1,R,pos,val); Mx[Now]=max(Mx[Now<<1],Mx[Now<<1|1]); } int query2(int Now,int L,int R,int l,int r) { if(l<=L&&r>=R) return Mx[Now]; int Mid=(L+R)>>1,res=-inf; if(l<=Mid) res=max(res,query2(Now<<1,L,Mid,l,r)); if(r>Mid) res=max(res,query2(Now<<1|1,Mid+1,R,l,r)); return res; } int main() { int N,M,P,L,R; char opt[4]; scanf("%d%d",&N,&M); rep(i,1,N<<2) Mn[i]=inf,Mx[i]=-inf; rep(i,1,N){ scanf("%d%d",&L,&R); update1(1,1,N,i,R); update2(1,1,N,i,L); } rep(i,1,M){ scanf("%s",opt); if(opt[0]=='?'){ scanf("%d%d",&L,&R); int mn=query1(1,1,N,L,R); int mx=query2(1,1,N,L,R); printf("%d\n",max(mn-mx+1,0)); } else { scanf("%d%d%d",&P,&L,&R); update1(1,1,N,P,R); update2(1,1,N,P,L); } } return 0; }View Code
J .Meme Wars
题意:字符串操作,每次F(x)=F(x-1)+'x'+F(x-1);问26次操作后,第N位是什么。(N<1000)
思路:因为N不大,所以我们保留前面N位,模拟即可。复杂度(N^2)
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1010; char c[maxn][maxn]; int L[maxn],N; int main() { scanf("%d",&N); c[1][1]='a'; L[1]=1; rep(i,2,27) { L[i]=L[i-1]; rep(j,1,L[i]) c[i][j]=c[i-1][j]; if(L[i]+1<=N) L[i]++,c[i][L[i]]='a'+i-1; rep(j,1,L[i-1]) { if(L[i]+1>N) break; c[i][++L[i]]=c[i][j]; } } printf("%c\n",c[27][N]); return 0; }View Code
H .Wine Production
题意:给定N个数,Q次询问,每次询问一个区间最大的x,满足至少x个不同的数出现了x次。(N,Q<3e4)
思路:跑莫队,然后记录每个数的个数。 问题是是莫队之后怎么维护这个答案。 我们用num[i]表示i出现的次数,cnt[i]表示出现次数为i的个数。 这样很难维护到有效信息,因为每次求答案的时候还是得x=1,=2..一个一个验证。 转化一下,cnt[i]表示出现次数大于等于i的个数,就可以莫队的时候直接转移答案了。 比如我加入一个数fcy, 那么num[fcy]++; cnt[num[fcy]]++;(而不用去cnt[num[fcy]-1]--;)因为出现次数大于等于num[fcy]-1的个数没变。 而加入fcy时,答案ans可能不变,可能变成了ans++变为了, cnt[num[fcy]]; 减去一个数fcy时也一样,ans可能不变,可能--; 想象成一个栈,如果操作中间,那么每次得重新维护这个栈。 而只操作栈顶,那么有效信息最多改变1,方便我们去维护。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; const int inf=1000000000; int a[maxn],b[maxn],cnt[maxn],num[maxn]; int ans[maxn],tot,B,res; struct qust{ int l,r,id; bool friend operator<(qust w,qust v){ if(w.r/B==v.r/B) return w.l<v.l; return w.r<v.r; } }q[maxn]; void add(int x,int tp) { if(tp==-1){ cnt[num[x]]--; num[x]+=tp; if(cnt[res]<res) res--;} else { num[x]+=tp;cnt[num[x]]++;if(cnt[num[x]]>=num[x]) res=max(res,num[x]);} } int main() { int N,M; scanf("%d%d",&N,&M); B=300; rep(i,1,N) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+N+1); tot=unique(b+1,b+N+1)-(b+1); rep(i,1,N) a[i]=lower_bound(b+1,b+tot+1,a[i])-b; rep(i,1,M) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; sort(q+1,q+M+1); int L=1,R=0; rep(i,1,M){ while(L<q[i].l) add(a[L++],-1); while(L>q[i].l) add(a[--L],1); while(R>q[i].r) add(a[R--],-1); while(R<q[i].r) add(a[++R],1); ans[q[i].id]=res; } rep(i,1,M) printf("%d\n",ans[i]); return 0; }View Code