来自FallDream的博客,未经允许,请勿转载,谢谢。
A.[cf577b]Modulo Sum
给定n个数,问是否有一个子序列相加的和取余m为0 n<=10^6,m<=1000
题解:很容易想到nmDP,(我写了暴力1950ms过了哈哈哈),另外m<n的时候肯定有可以整除m的,因为前缀和的余数最多只有m种。
代码丑,其实就是暴力加了一句话.....nmDP大家都会,就不重写了
#include<iostream> #include<cstdio> #include<cstring> 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 b[1005],n,m,f[1005][1005],mark[1005]; int main() { n=read();m=read(); if(m<n)return 0*puts("YES"); for(int i=1;i<=n;i++)b[read()%m]++; n=0; for(int i=0;i<m;i++)mark[i]=-1; for(int i=0;i<m;i++) { if(!b[i]) continue; ++n; for(int j=0;j<m;j++) f[n][j]|=f[n-1][j]; for(int k=1;k<=b[i];k++) { int th=k*i%m; if(mark[th]==i) break;mark[th]=i; f[n][th]=1; for(int j=0;j<m;j++) f[n][(th+j)%m]|=f[n-1][j]; // printf("%d ",th); } } if(f[n][0]) puts("YES"); else puts("NO"); return 0; }
B.[cf575b]Invariance of Tree
给定n个数pi,问是否能构造一棵树,对于每条边连接的i,j, pi和pj也有连边 n<=100000
题解:首先把i->pi连边,变成很多个环,如果存在一个点,那么全部连它即可。不然必须满足有一个长度为2的环,全部环都要是偶数,这样子就能对称的连起来。
#include<iostream> #include<cstdio> #include<cstring> #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; } int n,p[MN+5],q[MN+5],top=0,sz,len=-1; bool mark[MN+5]; int flag,only,odd; void work(int x) { int y=flag; for(;!mark[x];x=p[x],y=p[y]) printf("%d %d ",x,y),mark[x]=1; } int main() { n=read(); for(int i=1;i<=n;i++) p[i]=read(); for(int i=1;i<=n;i++) if(!mark[i]) { sz=1;mark[i]=1;q[++top]=i; for(int j=p[i];!mark[j];j=p[j]) mark[j]=1,sz++; if(sz==2) flag=i; else if(sz==1) only=i; else if(sz&1) odd=true; } if(only) { puts("YES"); for(int i=1;i<=n;i++)if(i!=only) printf("%d %d ",i,only); return 0; } if(odd||!flag)return 0*puts("NO"); puts("YES"); printf("%d %d ",flag,p[flag]); memset(mark,0,sizeof(mark)); mark[flag]=1,mark[p[flag]]=1; for(int i=1;i<=n;i++)if(!mark[i]) work(i); return 0; }
C.[cf651d]Image Preview
有n张,两种的照片围起来,一种照片看需要1s,另一种需要b+1s,你一开始在一号照片处,移动一次要a s,你只有Ts的时间,求你最多能看多少。
题解:显然情况只有三种,只看左右和同时看左右。前两种容易处理,剩下一种我们就二分一下最远点就行了。
#include<iostream> #include<cstdio> #include<cstring> #define ll long long #define MN 1000000 #define INF 200000000 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,a,b,ans=0;ll f[MN+5],T; char st[MN+5]; int get(ll x,int i) { // cout<<"get"<<x<<" "<<i<<endl; int l=i+1-n,r=n,mid,ans=INF; while(l<=r) { mid=l+r>>1; if(f[mid]<=x) ans=mid,r=mid-1; else l=mid+1; } return ans; } int get2(ll x,int i) { // cout<<"get2 "<<x<<" "<<i<<endl; int l=n+2,r=i+n-1,mid,ans=-INF; while(l<=r) { mid=l+r>>1; if(f[mid]<=x) ans=mid,l=mid+1; else r=mid-1; } return ans; } int main() { n=read();a=read();b=read();T=read(); scanf("%s",st+1); for(int i=1;st[i];i++) f[i]=f[i+n]=st[i]=='w'?b+1:1; for(int i=n;i;i--) f[i]+=f[i+1]+a; for(int i=n+2;i<=n<<1;i++) f[i]+=f[i-1]+a; for(int i=n+1;i<=n<<1;i++) { if(f[i]>T) break; ans=max(ans,i-n); int L=get(T-f[i]+f[n+1]-a*(i-n-1),i); // cout<<i<<" "<<L<<endl; ans=max(ans,i-L+1); } for(int i=n;i>=2;i--) { if(f[i]>T)break; ans=max(ans,n+2-i); int R=get2(T-f[i]+f[n+1]-a*(n+1-i),i); // cout<<i<<" "<<R<<endl; ans=max(ans,R-i+1); } printf("%d ",ans); return 0; }
D.[cf669d]Little Artem and Dance
一开始有n个数1-n,有两种变换,一种是全部移动,一种是把现在的12,34,56......位的数字相互交换,求最后的数字顺序 n,q<=1000000
题解:大模拟,移动容易处理,交换的话根据目前的状况特殊处理一下。
#include<iostream> #include<cstdio> #include<cstring> #define ll long long #define MN 1000000 #define INF 200000000 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 rev=0,move=0,n,m,change=0; int ans[1000005]; int main() { n=read();m=read(); for(int i=1;i<=m;i++) { int op=read(); if(op==1) move=(move+read()+n)%n; else { if(!rev) rev=(move&1)?1:2; else if(move&1) { if(rev==2) change+=1; rev=0; } else { if(rev==1) change-=1; rev=0; } } } for(int i=1;i<=n;i++) { int pos=i+move+change*((i&1)?2:-2);while(pos<n) pos+=n; if(rev){if((pos&1)^(move&1)==(rev&1))pos--;else pos++;} pos=(pos+n-1)%n+1; ans[pos]=i; } for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }
E.[cf687b]Remainers Game
给定k和n个数ai,你可以知道所有x%ai的值,求是否可以知道x%k的值。
题解:根据中国剩余定理,你可以猜出x%k的值,当且仅当存在m1,m2,m3..mm使得m1m2m3...mm=k且m1,m2,m3互质。所以分解x=p1^k1*p2^k2....,只要对于每个pi^ki都有数是他的倍数就行了。这样的话直接求lcm就好了
#include<iostream> #include<cstdio> #include<cstring> #define ll long long #define MN 1000000 #define INF 200000000 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,k,a[MN+5],mn=0; bool b[MN+5],has[MN+5]; int last[MN+5],num[MN+5]; inline ll gcd(ll x,ll y){return (!y)?x:gcd(y,x%y);} int main() { n=read();k=read();if(!k)return 0*puts("Yes"); for(int i=1;i<=n;i++) { a[i]=read(); if(a[i]%k==0)return 0*puts("Yes"); } ll ans=1; for(int i=1;i<=n;i++) ans=1LL*ans/gcd(ans,a[i])*a[i]%k; if(!ans)puts("Yes"); else puts("No"); return 0; }