原题连接
简洁描述:
一个数列有两种操作,一是两个数同时加或减一个数,二是两个数一个加一个减,求能不能从序列 a a a变成序列 b b b
分析:
这个题目可以发现如果两个数被赋予了操作二,则两个数可以互帮互助,可以组成和为
a
u
+
a
v
a_u+a_v
au+av的任意一个组合。这时候只要
b
u
+
b
v
b_u+b_v
bu+bv等于
a
u
+
a
v
a_u+a_v
au+av即可。
那么如果是操作一呢?我们可以发现,如果有两个操作一
(
u
,
v
)
(u,v)
(u,v)和
(
v
,
k
)
(v,k)
(v,k),则把第一个进行操作,第二个做第一个的逆操作,则变成了进行操作二的
(
u
,
k
)
(u,k)
(u,k)。于是,一个
u
u
u连接的一些
v
v
v,那么这些
v
v
v之间都是可以用操作二连接的。
考虑建图,
u
u
u向
v
v
v连无向边即可。那么回到操作二,这个操作没办法直接连边,所以可以设置一个点
k
k
k,
a
k
=
b
k
=
0
a_k=b_k=0
ak=bk=0,这样
k
k
k就是两个操作一中间的
v
v
v一样。就可以
u
→
k
u\to k
u→k和
v
→
k
v\to k
v→k连两条无向边即可。
再考虑建立操作二的关系。两个儿子之间都是需要连边的。为了判断两个点之间的关系,可以用并查集解决。于是就把所有儿子用并查集连起来。一个集合里面的和可以累加到一块,因为里面都是可以做出任何一种和为
∑
v
∈
V
a
v
\sum_{v\in V}a_v
∑v∈Vav的序列。当然,目标也要求和。
接下来考虑判断正确性。我们知道,
u
→
v
u\to v
u→v之间有边,则这两个是有一个操作一的。那么对于每一个
u
u
u就出现了三种情况:
- 断子绝孙:不能进行任何操作,所以只用判断 a = = b a==b a==b即可。
- 一家人:和儿子是同一个集合,则 u u u只能在这个集合内部进行偶数的加减,所以要保证 b − a b-a b−a是偶数。
- 陌生人:属于不同的集合。所以 u u u可以在两个集合一起加上或减去 1 1 1,则要求两个集合 a a a, b b b的差是相同的。
最后,每条边计算完还没有输出
N
O
NO
NO就是
Y
E
S
YES
YES了。
并查集时间复杂度很快,时间复杂度
O
(
n
)
O(n)
O(n),最快是
Ω
(
1
)
\Omega(1)
Ω(1),平均时间复杂度一般在
Θ
(
7
)
\Theta(7)
Θ(7)左右,所以可以当成常数看。总时间复杂度
Θ
(
n
+
m
)
\Theta(n+m)
Θ(n+m)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int NN=200004,MM=400004;
int n,m;
int tot,head[NN],to[MM],nxt[MM],fa[NN];
long long a[NN],b[NN];
struct node
{
int t,u,v;
}p[NN];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void add(int x,int y)
{
tot++;
nxt[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n+m;i++)
fa[i]=i;
memset(head,0,sizeof(head));
tot=0;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&p[i].t,&p[i].u,&p[i].v);
a[i+n]=b[i+n]=0;
if(p[i].t==1)
{
add(p[i].u,p[i].v);
add(p[i].v,p[i].u);
}
else
{
add(i+n,p[i].v);
add(p[i].v,i+n);
add(i+n,p[i].u);
add(p[i].u,i+n);
}
}
for(int i=1;i<=n+m;i++)
{
int k=0;
for(int j=head[i];j;j=nxt[j])
{
int v=to[j];
if(k)
{
int x=find(k),y=find(v);
if(x==y)
continue;
a[x]+=a[y];
b[x]+=b[y];
fa[y]=x;
}
k=v;
}
}
bool flag=true;
for(int i=1;i<=n;i++)
{
int j=to[head[i]];
if(!head[i])
{
if(a[i]!=b[i])
{
flag=false;
break;
}
continue;
}
int l=find(i),r=find(j);
if(l==r)
{
if((a[l]-b[l])&1)
{
flag=false;
break;
}
continue;
}
if(a[l]-b[l]!=a[r]-b[r])
{
flag=false;
break;
}
}
puts(flag?"YES":"NO");
}
return 0;
}