题目链接
首先我们发现,各个位置之间,$a$和$b$的原数值并不重要,所以我们可以令$b_ileftarrow b_i-a_i$,接下来只使用这个差值。
然后我们发现,2操作是可以使两个位置之间的差值进行“流动”。
所以,如果只有2操作,把每个位置看成点,差值是点权,那么2操作就是随意转运权值的无向边,2操作构成的连通块的点权能够全部变为$0$等价于点权和为$0$。
然后加入1操作,发现它可以使相邻的两个点的点权和增减$2$的倍数。
然后我们只会做2操作。
这里是个我的思维瓶颈,考试的时候没突破,人裂开。
这时候就体现了手玩数据的好处了。我一直没这个习惯,导致有些挺显然的结论看不出来。
然后可以发现,1操作构成的路径(不一定是简单路径)上,距离为奇数的两个点可以同增同减,距离为偶数的两个点可以转运权值。
思考一下,如果只有1操作,那么这个路径的性质就迫使我们进行染色。染色……二分图来了。
如果某个连通块是个二分图,或者说,没有奇环,那么只要两个分图的权值和相同,就可以对消了。
那有奇环呢?发现每条路径只需要绕一下这个奇环,就可以改变自身的奇偶性,那就是都可以随意转运权值,要连通块权值和为$0$咯?也不全对,因为我们还可以利用奇数长度路径的性质,对整个连通块的点权和增减$2$的倍数。也就是说只要连通块点权和是偶数,就可以使每个点的点权变为$0$。
然后考虑怎么联合两种操作。
我们发现2操作连出来的连通块,内部的权值是可以随意转运的,那么对于一条1边(1操作连出来的边),固定了一个端点,另一个端点连在某个2边连通图里,不论连哪个点都是等价的。那么我们可以对2边连通块进行缩点,然后1边就只连2边连通块就行了。如果出现1边连了同一个2边连通块,直接当做奇环就可以了。
讲完了。嗯。记得开long long,然后常数写好一点。
我交过一份95分的代码,超时一个点。然后删了一句数组初始化为$0$的memset就卡过了。
代码(100分):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<map> #include<set> #define IL inline #define RG register #define _1 first #define _2 second using namespace std; typedef long long LL; typedef unsigned int UI; const int N=1e5; int T,n,m; LL a[N+3]; int k[3],u[3][N+3],v[3][N+3]; vector<int>g[N+3]; bool flag; int cnt,bel[N+3]; LL sum[N+3]; void dfs1(int u){ for(UI i=0;i<g[u].size();i++){ int v=g[u][i]; if(bel[v]!=0) continue; bel[v]=bel[u]; sum[bel[u]]+=a[v]; dfs1(v); } } IL void sol1(){ for(int i=1;i<=k[2];i++){ g[u[2][i]].push_back(v[2][i]); g[v[2][i]].push_back(u[2][i]); } memset(bel,0,(n+3)*(sizeof(int))); cnt=0; for(int i=1;i<=n;i++) if(bel[i]==0){ bel[i]=++cnt; sum[cnt]=a[i]; dfs1(i); } flag=true; for(int i=1;i<=cnt&&flag;i++) if(sum[i]!=0) flag=false; if(flag) printf("YES "); } bool tag[N+3],ex; int col[N+3]; LL s[3]; void dfs2(int u){ for(UI i=0;i<g[u].size();i++){ int v=g[u][i]; if(col[v]!=0){ if(col[u]==col[v]) ex=true; continue; } col[v]=3-col[u]; if(tag[v])ex=true; s[col[v]]+=sum[v]; dfs2(v); } } IL void sol2(){ memset(tag,0,(cnt+3)*(sizeof(int))); for(int i=1;i<=cnt;i++) g[i].clear(); for(int i=1;i<=k[1];i++) if(bel[u[1][i]]==bel[v[1][i]]) tag[bel[u[1][i]]]=true; else { g[bel[u[1][i]]].push_back(bel[v[1][i]]); g[bel[v[1][i]]].push_back(bel[u[1][i]]); } memset(col,0,(cnt+3)*(sizeof(int))); flag=true; for(int i=1;i<=cnt;i++) if(col[i]==0){ ex=tag[i]; s[2]=0; col[i]=1; s[1]=sum[i]; dfs2(i); if(ex) flag&=((s[1]+s[2])&1)==0; else flag&=s[1]==s[2]; } if(flag) printf("YES "); else printf("NO "); } IL void sol(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=n;i++){ LL x; scanf("%lld",&x); a[i]=x-a[i]; g[i].clear(); } memset(g,0,sizeof g); k[1]=k[2]=0; for(int i=1;i<=m;i++){ int t; scanf("%d",&t); k[t]++; scanf("%d%d",&u[t][k[t]],&v[t][k[t]]); } flag=false; sol1(); if(flag)return ; sol2(); } int main(){ scanf("%d",&T); while(T--) sol(); return 0; }