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<cstdio> 2 #include<cstring> 3 #include<algorithm> 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 }