来自FallDream的博客。未经允许,请勿转载,谢谢。
----------------------------------------------------
和ditoly组队打vkcup,原来以为是正常div2难度,结果发现.....不说了 AB都被叉了一遍,又掉分 .......
-------------------------
A.给定n个人和m组朋友关系,判断这个图是否满足性质:如果x和y是朋友,y和z是朋友,那么x和z是朋友 n,m<=150000
题解:很容易发现满足关系的图是团,所以我们对每个联通块判一下就可以了。
n的范围很奇怪,我没注意,结果被hack了,发现150000*149999会爆int....
#include<iostream> #include<cstdio> #include<cmath> #include<queue> #include<cstring> #include<algorithm> #define ll long long 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 cnt=0,n,m; struct edge{ int to,next; }e[300005]; int head[300005]; ll num,sum; bool mark[300005]; inline void ins(int u,int v) { e[++cnt].next=head[u];head[u]=cnt; e[cnt].to=v; } void dfs(int x) { mark[x]=1;num++; for(int i=head[x];i;i=e[i].next,sum++)if(!mark[e[i].to]) dfs(e[i].to); } int main() { n=read();m=read(); for(int i=1;i<=m;i++) {int a=read();int b=read();ins(a,b);ins(b,a);} for(int i=1;i<=n;i++)if(!mark[i]) {num=sum=0;dfs(i); if(num>2&&sum!=(num-1)*num)return 0*puts("NO"); // if(num==2&&sum!=2)return 0*puts("NO"); } puts("YES"); return 0; }
B.给定n,k,表示有n个名字,然后给定n-k+1个条件,每个条件是YES/NO,表示从它开始的连续k个名字有没有相同的(YES表示没有),你要构造出满足题意的名字,保证有解。n<=50
题解:我们先把全部都赋成不同的名字,然后从后往前,如果第i个是NO,那么把它变成和i+k-1一样。
#include<iostream> #include<cstdio> #include<vector> #include<cstring> #define ll long long 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 k,cnt=0,n; string name[55],s; char st[555]; int res[55]; string getnewname() { cnt++;s="A"; for(int i=cnt;i>0;i-=26)s=s+(char)((i-1)%26+'a'); return s; } int pd(){scanf("%s",st+1);return st[1]=='Y'?1:0;} int main() { n=read();k=read(); for(int i=1;i+k-1<=n;i++) res[i]=pd(); for(int i=1;i<=n;i++)name[i]=getnewname(); for(int i=n-k+1;i;i--)if(res[i]) name[i]=getnewname(); else name[i]=name[i+k-1]; for(int i=1;i<=n;i++)cout<<name[i]<<" "; return 0; }
C.有一棵树,每条边的权值是1,然后给定k,表示你一步可以走的距离为k。令Fst表示从s到t要走多少步,求Fij,1<=i<j<=n n<=200000 k<=5
题解:乱dp,一个表示走多少步,另一个表示没走满一步的个数。
#include<cstdio> #include<iostream> using namespace std; char B[1<<26],*S=B,C;int X; inline int read() { while((C=*S++)<'0'||C>'9'); for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0'; return X; } #define MN 200000 struct edge{int nx,t;}e[MN*2+5]; int k,h[MN+5],en,f[MN+5][5]; long long ans,ff[MN+5]; inline void ins(int x,int y) { e[++en]=(edge){h[x],y};h[x]=en; e[++en]=(edge){h[y],x};h[y]=en; } void dfs(int x,int fa) { int i,j,l; f[x][0]=1; for(i=h[x];i;i=e[i].nx)if(e[i].t!=fa) { dfs(e[i].t,x); for(j=0;j<k;++j) { ans+=1LL*f[e[i].t][j]*ff[x]+1LL*f[x][j]*ff[e[i].t]; for(l=0;l<k;++l)ans+=1LL*f[e[i].t][j]*f[x][l]*((j+l)/k+1); } for(j=1;j<k;++j)f[x][j]+=f[e[i].t][j-1]; f[x][0]+=f[e[i].t][k-1];ff[x]+=ff[e[i].t]+f[e[i].t][k-1]; } } int main() { fread(B,1,1<<26,stdin); int n=read(),i;k=read(); for(i=1;i<n;++i)ins(read(),read()); dfs(1,0); cout<<ans; }
D.给定一个长度为n的字符串,每次你可以交换一对连续的字符,求最少的交换次数,使得字符串中没有连续的两个是"VK" n<=75
题解:我们把不是VK的字符看作一种,然后用f[i][j][k][0/1]表示前i个V,j个K,k个不是VK的字符,最后一位是/不是V的最小交换数。
我们枚举最后一位放什么,那么它需要额外的交换次数是前i个V,j个K,k个其他字符中在它之后的个数(因为那些要从后面移到前面),然后就可以转移啦。
#include<iostream> #include<cstdio> #include<vector> #include<cstring> #define ll long long 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,k,o; int n; char st[100]; ll f[77][77][77][2]; int main() { n=read();memset(f,127,sizeof(f)); scanf("%s",st+1); for(int i=1;i<=n;i++) if(st[i]=='V') v.push_back(i); else if(st[i]=='K')k.push_back(i); else o.push_back(i); f[0][0][0][0]=0; for(int i=0;i<=v.size();i++) for(int j=0;j<=k.size();j++) for(int l=0;l<=o.size();l++) for(int c=0;c<=2;c++) { if((!i)&&(!c))continue; if((!j)&&c==1)continue; if((!l)&&c==2)continue; int pos,num=0; if(!c) pos=v[i-1]; else if(c==1) pos=k[j-1]; else pos=o[l-1];//cout<<pos<<endl; for(int m=i;m<v.size()&&v[m]<pos;m++)num++;//cout<<num<<endl; for(int m=j;m<k.size()&&k[m]<pos;m++)num++;//cout<<num<<endl; for(int m=l;m<o.size()&&o[m]<pos;m++)num++; // cout<<num<<endl; if(c==0)for(int m=0;m<=1;m++) f[i][j][l][1]=min(f[i][j][l][1],f[i-1][j][l][m]+num); if(c==1)for(int m=0;m<=0;m++) f[i][j][l][0]=min(f[i][j][l][0],f[i][j-1][l][m]+num); if(c==2)for(int m=0;m<=1;m++) f[i][j][l][0]=min(f[i][j][l][0],f[i][j][l-1][m]+num); // cout<<i<<" "<<j<<" "<<l<<" "<<f[i][j][l][0]<<" "<<f[i][j][l][1]<<endl; } int a=v.size(),b=k.size(),c=o.size(); cout<<min(f[a][b][c][0],f[a][b][c][1]); return 0; }
E.给定一个2*n的矩阵,你要选出最多的和为0且不相交的矩阵,求出最多的数量。n<=300000
题解:记忆化搜索,每次选择比较靠后的往前跳,貌似是卡不掉的。
#include<cstdio> #include<algorithm> #include<map> using namespace std; #define ll long long char B[1<<26],*S=B,C;ll X,F; inline ll read() { for(F=1;(C=*S++)<'0'||C>'9';)if(C=='-')F=-1; for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0'; return X*F; } #define MN 300000 #define d(x,y) make_pair(x,y) ll s[MN+5][3]; int t[MN+5][3],f[MN+5]; map<ll,int> mp; map<pair<int,int>,int> u,ff; int cal(int x,int y) { if(u[d(x,y)])return ff[d(x,y)]; return u[d(x,y)]=1,ff[d(x,y)]=max(f[min(x,y)],t[x][0]>t[y][1]?cal(t[x][0],y)+1:cal(x,t[y][1])+1); } int main() { fread(B,1,1<<26,stdin); int n=read()+1,i,j,k,l; for(i=2;i<=n;++i)s[i][0]=s[i-1][0]+read(); for(i=2;i<=n;++i)s[i][1]=s[i-1][1]+read(),s[i][2]=s[i][0]+s[i][1]; for(l=0;l<3;++l)for(mp.clear(),i=1;i<=n;++i)t[i][l]=max(t[i-1][l],mp[s[i][l]]),mp[s[i][l]]=i; f[0]=t[0][0]=t[0][1]=-1;u[d(0,0)]=1;ff[d(0,0)]=-2; for(i=2;i<=n;++i)f[i]=max(f[t[i][2]]+1,cal(i,i)),ff[d(i,i)]=f[i]; printf("%d",f[n]); }