思路:
并查集维护每个开关的状态on[i]和off[i] 。
假设灯L由开关S1和S2控制。
如果开关是亮的,则S1和S2的状态相反;
如果开关是灭的,则S1和S2的状态相同。
当一个开关状态已知时,可以得知另一个开关的状态,合并。
如果on[i]和off[i]在同一个集合就无解。
时间复杂度:O((n+m)α(n))。
当然也可以二分图判定。
1 #include<cstdio> 2 #define on(i) i 3 #define off(i) i+m 4 const int M=100001,N=100001; 5 class DisjointSet { 6 private: 7 int anc[M<<2]; 8 int Find(const int x) { 9 return (x==anc[x])?x:(anc[x]=Find(anc[x])); 10 } 11 public: 12 DisjointSet(const int m) { 13 for(int i=1;i<=(m<<1);i++) { 14 anc[i]=i; 15 } 16 } 17 void Union(const int x,const int y) { 18 anc[Find(x)]=Find(y); 19 } 20 bool isConnected(const int x,const int y) { 21 return Find(x)==Find(y); 22 } 23 }; 24 int r[N]; 25 int l[N][2]={0}; 26 int main() { 27 int n,m; 28 scanf("%d%d",&n,&m); 29 for(int i=1;i<=n;i++) { 30 scanf("%d",&r[i]); 31 } 32 for(int i=1;i<=m;i++) { 33 int x; 34 scanf("%d",&x); 35 while(x--) { 36 int d; 37 scanf("%d",&d); 38 l[d][l[d][0]?1:0]=i; 39 } 40 } 41 DisjointSet s(m); 42 for(int i=1;i<=n;i++) { 43 if(!r[i]) { 44 s.Union(on(l[i][0]),off(l[i][1])); 45 s.Union(on(l[i][1]),off(l[i][0])); 46 } 47 else { 48 s.Union(on(l[i][0]),on(l[i][1])); 49 s.Union(off(l[i][0]),off(l[i][1])); 50 } 51 } 52 for(int i=1;i<=m;i++) { 53 if(s.isConnected(on(i),off(i))) { 54 puts("NO"); 55 return 0; 56 } 57 } 58 puts("YES"); 59 return 0; 60 }