NOI ONLINE 提高组 序列 根据性质建图

题目链接

  https://www.luogu.com.cn/problem/P6185

题意

  应该不难懂,跳过

分析

  说实话第一眼看到这题的时候我有点懵,真不知道怎么做,不过一看数据,还好还好,暴力能拿一半分,于是我就真拿了一半分。。。。。

  但某大佬说暴力能拿60,但我拿一半就满意了   我不会啊

  考完后忍不住好奇这道题要怎么做,于是就看了看题解,发现题解也。。。有点难懂,主要是我看到一个字,图??这明明是个数的问题咋还和图扯上了关系,awsl,果然还是我太       

     仔细读了一下,明白了一些。先看操作一:如果有(a1,a2)(a2,a3)(a3,a1),那么其中任意一个数都能自己加减二,如a1,a1+1,a2+1,a2-1,a3-1,a3+1,a1+1这样就能让a1自己加减二,同理a1换成任何数都可以,这里要注意,必须是奇数个点并且形成环才能这样办,所以每个奇数环上的数都能加减二,偶数个点为什么不行自己举个例子就明白了。再看操作二:如果有(a1,a2)(a2,a3)那么可以知道(a1,a3),a1+1,a2-1,a2+1,a3-1由此,a1+1,a3-1,可见操作二是具有传递性的,如果把它们看做是一个联通块,那么这个联通块可以任意加1,减1,所以如果这个联通块需要加的值和需要减的值一样,那么就满足。于是我们只要把每个操作二都缩成点,每个操作一建边,然后开始判断每块联通块是不是满足题意。

判断方法为,如果未形成奇数环,则需要使联通块内相加的数与相减的数相等,因为只能加一减一,否则使需要变化的总数是偶数即可,注意自环也要判断,因为自环相当于(a1,a2,1)(a1,a2,2)即a1+1,a2+1,a2-1,a1+1,这样也能使任意数加减二,

然后还有就是对于没有边连入的点,只有需要变化的值为0,才满足,因为没有边可以使它产生变化。

 1 #include
 2 #include
 3 #include
 4 using namespace std;
 5 const int N=2e5+10;
 6 long long val[N];
 7 struct Edge{
 8     int to,next;
 9 }e[N];
10 struct Node{
11     int t,u,v;
12 }p[N];
13 int Head[N],len,sum,flag;
14 void Ins(int a,int b){
15     e[++len].to=b;e[len].next=Head[a];Head[a]=len;
16 }
17 int f[N],belong[N],a[N],b[N];
18 int find(int x){
19     return f[x]==x?x:(f[x]=find(f[x]));
20 }
21 void Mer(int a,int b){
22     int u=find(a),v=find(b);
23     if(u!=v){
24         f[u]=v;val[v]+=val[u];//并查集缩点
25     }
26 }
27 void dfs(int x,int bin){
28     belong[x]=bin;
29     if(bin)sum+=val[x];
30     else sum-=val[x];
31     for(int i=Head[x];i;i=e[i].next){
32         int v=e[i].to;
33         if(belong[v]==-1)dfs(v,bin^1);//利用了^的性质
34         else if(belong[x]==belong[v])//说明v已经被提前访问过并且bin的值与x一样,那么就一定形成了奇数环
35             flag=1;
36     }
37 }
38 int main(){
39 //    freopen("a.txt","r",stdin);
40     int t;
41     scanf("%d",&t);
42     while(t--){
43         int m,n,ans=1;len=0;
44         scanf("%d%d",&n,&m);
45         for(int i=1;i<=n;i++)
46             scanf("%d",&a[i]);
47         for(int i=1;i<=n;i++)
48             scanf("%d",&b[i]),val[i]=b[i]-a[i];
49         for(int i=1;i<=n;i++)
50             f[i]=i,belong[i]=-1,Head[i]=0;
51         for(int i=1;i<=m;i++){
52             scanf("%d%d%d",&p[i].t,&p[i].u,&p[i].v);
53             if(p[i].t==2){
54                 Mer(p[i].u,p[i].v);//合并操作二
55             }
56         }
57         for(int i=1;i<=m;i++){
58             if(p[i].t==1){
59                 Ins(find(p[i].u),find(p[i].v));//这里已经缩点所以要合并根
60                 Ins(find(p[i].v),find(p[i].u));
61             }
62         }
63         for(int i=1;i<=n;i++){
64             if(find(i)==i&&belong[i]==-1){
65                 flag=sum=0;
66                 dfs(i,0);
67                 for(int x=Head[i];x;x=e[x].next){
68                     if(e[x].to==i)flag=1;//自环
69                 }
70                 if(Head[i]==0&&sum!=0)ans=0;//没有边连入
71                 else if(flag==1&&sum%2!=0)ans=0;//奇数环
72                 else if(flag==0&&sum!=0)ans=0;//偶数环
73             }
74         }
75         if(ans)printf("YES\n");
76         else printf("NO\n");
77     }
78 }

 

你可能感兴趣的:(NOI ONLINE 提高组 序列 根据性质建图)