https://file.floj.tech/export/eoAlAVCaKW099bc9d509
t1
【题解】
题目求亮着的灯有几个连续段。
答案可以转化为亮着的灯个数减去相邻的亮着的灯的对数。
考虑如何求相邻亮着的灯的对数。
求出每种颜色相邻哪几种颜色并连边,权值为这两种颜色相邻对数。
则答案就是总点数减去亮着的点的图的边权和。
由于某些颜色出度较多,直接枚举要T。
所以对于所有出度小于sqrt n的点暴力枚举修改并记录他们与出度大的点造成的贡献,在该出度大的点亮时在加上该贡献。
对于出度大的点只枚举他与其他出度大的点的边即可。
代码如下:
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,m,q,a[N],dahu[N],kuaida,siz[N],jia[N]; struct pigu { int q,dao; pigu(int x=0,int y=0) { dao=x;q=y; } friend bool operator <(pigu x,pigu y) { return x.dao<y.dao; } }; vector <pigu> ma[N],hu,ma2[N]; inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } bool book[N],xiao[N]; int main() { // freopen("light.in","r",stdin); // freopen("light.out","w",stdout); n=read();m=read();q=read();kuaida=sqrt(n); for(int i=1;i<=n;i++) a[i]=read(); int tot=0; for(int i=1;i<=n;i++) if(a[i]!=a[i-1]) dahu[++tot]=a[i]; for(int i=1;i<=tot;i++) a[i]=dahu[i],siz[a[i]]++; n=tot; for(int i=2;i<=tot;i++) ma[a[i]].push_back(pigu(a[i-1],1)),ma[a[i-1]].push_back(pigu(a[i],1)); for(int i=1;i<=m;i++) { int len=ma[i].size(); if(!len) continue; sort(ma[i].begin(),ma[i].end()); int ge=0; hu.clear(); hu.push_back(pigu(ma[i][0].dao,1)); for(int j=1;j<len;j++) { if(ma[i][j].dao==ma[i][j-1].dao) hu[ge].q++; else ge++,hu.push_back(pigu(ma[i][j].dao,1)); } ma[i].clear(); for(int j=0;j<=ge;j++) ma[i].push_back(hu[j]); } for(int i=1;i<=m;i++) { int len=ma[i].size(); if(len<kuaida) xiao[i]=1; } for(int i=1;i<=m;i++) if(xiao[i]==0) { int len=ma[i].size(); for(int j=0;j<len;j++) if(xiao[ma[i][j].dao]==0) ma2[i].push_back(ma[i][j]); } int ans=0,jian=0; while(q--) { int x=read(); int pan=book[x]?-1:1; book[x]^=1; ans+=siz[x]*pan; if(xiao[x]==1) { int len=ma[x].size(); for(int i=0;i<len;i++) { if(xiao[ma[x][i].dao]==0) jia[ma[x][i].dao]+=ma[x][i].q*pan; if(book[ma[x][i].dao]==0) continue; jian+=ma[x][i].q*pan; } } else { int len=ma2[x].size(); for(int i=0;i<len;i++) { if(book[ma2[x][i].dao]==0) continue; jian+=pan*ma2[x][i].q; } jian+=pan*jia[x]; } cout<<ans-jian<<" "; } fclose(stdin); fclose(stdout); return 0; }
t2
【题解】
可以对于每个相邻的不为0的点连边边权为他们的值的差。发现如果形成了环,这环上权值和必定为周期的倍数。
所以就dfs找环,找到就求gcd即可。
代码如下:
#include<bits/stdc++.h> using namespace std; int n,m,now[100005],dis[100005],last[100005],size; struct pigu { int dao,ne,quan; }a[200005]; inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } inline void lingjiebiao(int x,int y,int z) { a[++size].dao=y; a[size].quan=z; a[size].ne=last[x]; last[x]=size; } inline int gcd(int x,int y) { if(x<0) x=-x;if(y<0) y=-y; if(x==0||y==0) return x|y; return x%y==0?y:gcd(y,x%y); } int ans=0; bool book[100005]; inline void dfs(int no,int d) { if(book[no]) { ans=gcd(ans,d-dis[no]); return; } book[no]=1; dis[no]=d; for(int i=last[no];i;i=a[i].ne) { dfs(a[i].dao,d+a[i].quan); } } int main() { // freopen("crossing.in","r",stdin); // freopen("crossing.out","w",stdout); n=read();m=read(); for(int i=1;i<=m;i++) { int sh=0; for(int j=1;j<=n;j++) { now[j]=read(); if(now[j]==0) continue; if(sh) lingjiebiao(sh,j,now[j]-now[sh]),lingjiebiao(j,sh,now[sh]-now[j]); sh=j; } } for(int i=1;i<=n;i++) if(!book[i]) dfs(i,0); if(!ans) cout<<"-1"; else cout<<ans; fclose(stdin);fclose(stdout); return 0; } /* 4 5 0 33 0 36 0 4 0 7 42 2 42 5 0 21 0 24 8 0 8 54 */
t3
【题解】
发现到了最后图就分成很多块,每块中都有一部分人可以在自己块中乱走,但人数不够走到前头。
定义f[i][j]表示在第i个密室内有j个人时,前头最多有好多个人。在转移时必须保证右边的人不能走到左边来,都是左边人走到右边或者右边有一些人但走不过来,否则就会有m个人在1中。
注意下文=都是表示取max。
当j<a[i]时,在下一间有b[i]个人时,这些人可以走过去,f[i+1][b[i]+j]=f[i][j]+b[i]。
当j<a[i]时,设mx=max(f[i][j]),则下一间本身有<b[i]个人时,f[i+1][j]=mx+j。
当a[i]≤j<a[i]+b[i]时,可以走j-a[i]个人到下一间,f[i+1][j-a[i]]=f[i][j]。
当j>=a[i]+b[i]时,所有人都可以到下一间,f[i+1][j]=f[i][j]。
还有一个性质就是任意一间密室的人不能多于前面最大的a[i]+b[i],否则人全部跑了。这就保证了j<=20000。可以过。
代码如下:
#include<bits/stdc++.h> using namespace std; const int N=1005; int f[N][20005],da,n,m,a[N],b[N]; inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } int main() { //freopen("escape.in","r",stdin); //freopen("escape.out","w",stdout); n=read();m=read(); int ans=0; for(int i=1;i<=n-1;i++) a[i]=read(),b[i]=read(); da=m-1; for(int i=0;i<=m-1;i++) f[1][i]=i; for(int i=1;i<=n-1;i++) { int yj=0; for(int j=0;j<a[i];j++) f[i+1][b[i]+j]=max(f[i+1][b[i]+j],f[i][j]+b[i]),yj=max(yj,f[i][j]); for(int j=0;j<b[i];j++) f[i+1][j]=max(f[i+1][j],yj+j); for(int j=a[i];j<a[i]+b[i];j++) f[i+1][j-a[i]]=max(f[i+1][j-a[i]],f[i][j]); for(int j=a[i]+b[i];j<=da;j++) f[i+1][j]=max(f[i+1][j],f[i][j]); da=max(da,a[i]+b[i]-1); } ans=0; for(int i=0;i<=da;i++) ans=max(ans,f[n][i]); cout<<ans; return 0; }