T1 : 序列
题意:
一共有T组数据,每组数据有两个长度为n的序列a,b,m个操作,问a序列是否可以转换成b,是输出YES,否的话输出NO。m个操作分别为ti,xi,yi,若t为1,则x和y上的数可同时加减一;若t为2,则x上的数加一同时y上的数减一,或y上的数加一同时x上的数减一;
大概理解为: 将a上的所有数变成b上的对应的数,当t=1时可以将a[x],a[y]同时加任何数,当t=2时可以将a[x]加任何数同时a[y]上的数减上相同的数(相当于把a[x]一部分值转到a[y]上);
列: a : 1 3 5 4 t=1,x=1,y=2 则若a[1] (1) 要加上3,a[2] (3) 也必须加上3,a 变成 4 6 5 4
t=2,x=1,y=2 则若a[1] (1) 要加上3,a[2] (3) 必须减去3,a变成 4 0 5 4;
数据范围:
1≤T≤10,1≤n,m≤105,1≤ai,bi≤109
分析:
这题可以用图论来做
先定义一个序列 c ,c[i]=a[i]-b[i],这样我们只要判断c中的每一个值是否可以变为零就行了。
然后读入m个操作
先不用管t为1时的情况,若 t 为2的话,把 x 和 y 之间连一条无向边,然后将图中的联通块缩点。因为联通块中的任何俩个数都可以将他们的一部分值互相转换,所以可以将他们看做一个点,该点的权值就是联通块中的每一个c[i]的和。
下面记 i 点所在的联通块为bel[i]。
接下来再操作将t为1时的情况, 将bel[x]与bel[y]之间连一条无向边,若bel[x]=bel[y]则用一个bool数组表示bel[x]有自环,有自环后bel[x]的值就可以成2的倍数增长。
接下来就对新图进行缩点,可以发现,若两个点之间的路径长度为偶数时,两个点间可进行值的转移。
所以我们再将图进行黑白染色,同色点之间一定长度为偶数的路径,那么我们就将颜色相同的点进行缩点。
缩到最后,我们再将判断一下每个联通块是否可以将其的总权值变为零,如果有一个联通块不能将其总权值变为零,则输出NO,反之输出YES。
我们再看看如何判断联通块之间是否可以将其总权值变为零。
因为缩点缩到最后一定每个联通块最多只有两个点(黑点和白点),所以判断一下这两个点可不可以变为零就行了。
只要两个点的权值相同,那么就可以两个点同时加减变为零。如果不相等,如果两个点其中一个有自环,且两个点权值差为偶数,那就可以将两个点的值变相同,之后再进行操作。
因为我们只需要判断联通块是否可以变成零就行了,所以不用建新图,dfs一下就行。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 7 using namespace std; 8 9 typedef long long LL; 10 11 const int N=1000010; 12 13 LL sum[N]; 14 int bel[N],a[N],color[N]; 15 bool book[N]; 16 17 vector<int> h[N],g[N]; 18 19 struct Node 20 { 21 int x,y; 22 }w[N]; 23 24 void dfs1(int u,int c)//第一次缩点 (t为2时的边) 25 { 26 bel[u]=c; 27 sum[c]+=a[u]; 28 for(int i=0;i<g[u].size();i++) 29 { 30 int j=g[u][i]; 31 if(!bel[j]) 32 dfs1(j,c); 33 } 34 } 35 36 bool dfs2(int u,int col,bool &pd,LL &suma,LL &sumb)//第二次缩点 37 { 38 if(~color[u]) return color[u]==col;//若颜色不一样说明有奇数环 39 40 color[u]=col;//进行染色 41 pd|=book[u];//判断有没有自环 42 43 if(col==0) suma+=sum[u];//将相同色的点权值相加 44 else sumb+=sum[u]; 45 46 bool t=true; 47 48 for(int i=0;i<h[u].size();i++) 49 { 50 int j=h[u][i]; 51 52 t&=dfs2(j,col^1,pd,suma,sumb); 53 } 54 55 return t; 56 } 57 58 int main() 59 { 60 int T; 61 scanf("%d",&T); 62 while(T--) 63 { 64 int n,m; 65 int cnt_all=0,cnt=0; 66 67 scanf("%d%d",&n,&m); 68 69 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 70 71 for(int i=1;i<=n;i++) 72 { 73 int x; 74 scanf("%d",&x); 75 a[i]=x-a[i]; 76 77 g[i].clear();//将边初始化 78 h[i].clear(); 79 } 80 81 memset(w,0,sizeof(w)); 82 83 while(m--) 84 { 85 int p,x,y; 86 87 scanf("%d%d%d",&p,&x,&y); 88 89 if(p==1) 90 { 91 w[cnt_all++]={x,y};//存储当t为1时的边 92 } 93 else 94 { 95 g[x].push_back(y),g[y].push_back(x);//t为2时将x与y之间连边 96 } 97 } 98 99 memset(sum,0,sizeof(sum));//联通块总权值初始化 100 memset(bel,0,sizeof(bel));//每个点属于的联通块 101 102 for(int i=1;i<=n;i++) 103 { 104 if(!bel[i]) dfs1(i,++cnt); 105 } 106 107 memset(book,0,sizeof(book)); 108 109 for(int i=0;i<cnt_all;i++) 110 { 111 int x=bel[w[i].x],y=bel[w[i].y]; 112 113 if(x==y) book[x]=true;//判断自环 114 else 115 { 116 h[x].push_back(y);//不是自环则加边 117 h[y].push_back(x); 118 } 119 } 120 121 bool ans=1; 122 123 memset(color,-1,sizeof(color));//初始化颜色 124 125 for(int i=1;i<=cnt;i++) 126 { 127 if(color[i]==-1)//若没被染过色 128 { 129 bool pd=false;//判断自环 130 LL suma=0,sumb=0;//黑色和白色点的权值总和 131 132 if(dfs2(i,0,pd,suma,sumb)) 133 { 134 if(pd) ans&=(suma+sumb)%2==0;//判断联通块权值是否可以变为零 135 else ans&=suma==sumb; 136 } 137 else 138 { 139 ans&=(suma+sumb)%2==0; 140 } 141 } 142 } 143 144 if(ans) puts("YES"); 145 else puts("NO"); 146 } 147 }