NOI Online #1 提高组 T1 序列

原题连接

简洁描述:

一个数列有两种操作,一是两个数同时加或减一个数,二是两个数一个加一个减,求能不能从序列 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 uk v → k v\to k vk连两条无向边即可。
再考虑建立操作二的关系。两个儿子之间都是需要连边的。为了判断两个点之间的关系,可以用并查集解决。于是就把所有儿子用并查集连起来。一个集合里面的和可以累加到一块,因为里面都是可以做出任何一种和为 ∑ v ∈ V a v \sum_{v\in V}a_v vVav的序列。当然,目标也要求和。
接下来考虑判断正确性。我们知道, u → v u\to v uv之间有边,则这两个是有一个操作一的。那么对于每一个 u u u就出现了三种情况:

  • 断子绝孙:不能进行任何操作,所以只用判断 a = = b a==b a==b即可。
  • 一家人:和儿子是同一个集合,则 u u u只能在这个集合内部进行偶数的加减,所以要保证 b − a b-a ba是偶数。
  • 陌生人:属于不同的集合。所以 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;
}
©️2020 CSDN 皮肤主题: 我行我“速” 设计师:Amelia_0503 返回首页