素材巴巴 > 程序开发 >

AcWing-奇偶游戏-并查集

程序开发 2023-09-07 06:50:00

彼岸花开开彼岸 断肠草愁愁断肠 奈何桥前可奈何 三生石前定三生

 题意:给一个n表示一个01序列有多长,一个m,下面是m行,每行两个数字l, r,还有一个英文单词,表示l到r区间内有奇数还是偶数个1,求到第几行出现矛盾。矛盾即不合法,比如[1,4]内有偶数个1,[5, 8]内有奇数个1,但是下一行是[1,8]内有偶数个1,这显然是不合法的

做法一:带权并查集

首先看数据,发现可以用离散化,然后通过离散化处理输入的数据x,y。

unordered_map s;
 int n, m;
 int get(int x)
 {if(s.count(x) == 0) s[x] = ++n;return s[x];
 }
 

 通过观察我们发现,可以通过前缀和的思想,比如s[l - r] 有偶数个1,那么sum[l-1]和sum[r]奇偶性相同,反之,s[l - r]有奇数个1,那么sum[l-1]和 sum[r]的奇偶性相反,因为sum[l-1]+s[l - r]=sum[r],然后奇+奇=偶,偶+偶=偶,奇+偶=奇。那我们的输入的数据,表示编号,表示前缀和。

然后我们用d数组记录并表示边权,用p数组表示并查集的首领,如果d[x] = 0,(这个x相当于前边的sum数组), 则表示x与p[x]的奇偶性相同,如果为1,则表示不同。那么在一个集合里我们就可以通过根节点知道所有的子节点之间的关系,比如x到root的距离为dx,y到root的距离为dy,那么(dx+dy)% 2就可以表示是否他俩性质相同了。然后相同为0,不同为1,我们就非常自然地想到了可以用异或来代替运算,加权并查集放于Find函数中

int find(int x)
 {if (p[x] != x){int root = find(p[x]);d[x] ^= d[p[x]];p[x] = root;}return p[x];
 }

此处的^也可以写成+,因为奇+奇=偶,偶+偶=偶,奇+偶=奇,取模2效果是一样的。

下一步来判断矛盾:

如果x和y是同类

如果px==py,即他俩属于同一个集合,那么就可以通过根节点找子节点的关系,也就是dx+dy,如果结果与我们输入的字符串的状态一致,就可以return i-1,否则继续查找;dx+dy也可以写成dx^dy,因为dx+dy如果是奇数,那么就是dx和dy一个是奇数一个是偶数,也就是一个是1一个是0,取异或值的话,得到的结果是一样的;那么dx^dy==0时,说明xy是同类,然后 py与px是同类,所以不矛盾;反之如果dx^dy==1时,说明xy是同类,然后px与py不是同类,所以矛盾

如果px != py,即他们不属于一个集合,那么我们需要把他们拉进一个集合里,p[px] = py,这时px和py的d数组的关系也需要界定,那就是dx+?+dy=0,(0代表对2取模为0, 表示同类)也就是?+(dx+dy)= 0,那么就是?^dx^dy==0, 那么? == dx^dy^0(异或的一个公式)也就是d[px]=dx^dy^0(x的头儿的头儿是py,那么px到py的关系就是dx^dy^0)

如果x和y是异类

同上,当px==py时,dx^dy=1是不矛盾,dx^dy=0时,矛盾

当px!=py时,d[px] = dx^dy^1

然后就可以两类合并一起写,变成

#pragma GCC optimize(2)
 #include 
 using namespace std;
 typedef long long ll;
 #define mm(a) memset(a, 0, sizeof(a))
 #define PI acos(-1)
 #define LLu unsigned long long
 #define PLL pair
 #define PII pair
 #define xx first 
 #define yy second 
 ll gcd(ll a, ll b) {return b ? gcd(b, a%b) : a; }
 ll lcm(ll a, ll b) {return a/gcd(a, b)*b;}
 const int INF = 0x3f3f3f3f;
 const int N = 4e4 + 7;int n, m;
 int d[N], p[N];
 unordered_maps;int get(int x)
 {if(s.count(x) == 0) s[x] = ++n;return s[x];
 }
 int Find(int x)
 {if(x != p[x]){int root = Find(p[x]);d[x] ^= d[p[x]];p[x] = root;}return p[x];
 }
 int main()
 {cin >> n >> m;n = 0;for(int i = 0; i <= N; i ++) p[i] = i;int res = m;for(int i = 1; i <= m; i ++){int x, y;string type;cin >> x >> y >> type;x = get(x - 1), y = get(y);int t = 0;if(type == "odd") t = 1;int px = Find(x), py = Find(y);if(px == py){if(d[x]^d[y] != t){res = i - 1;break;}   }else{p[px] = py;d[px] = d[x]^d[y]^t;}}cout << res << endl;return 0;
 }

做法二:扩展域求并查集

好哒,我们再次观察数据,发现他又大又小,所以可以用离散化,然后数据就被缩小了,那么我们就可以考虑枚举的思想。两种状态奇偶,也就是一个点有两种状态,那么我们就可以用并查集反集来拆点,表示两种状态。操作就是取Base=边界值N,然后N~2*N范围内就是与我们的已知点状态相反的状态(所以数组要开两倍)

开始枚举:

如果输入的字符串表示偶数,也就是x的状态等于y的状态,那么如果x的反状态等于y则矛盾,所以

if (find(a + Base) == find(b)){res = i - 1;break;
 }

如果x的反状态不等于y的状态,也就是x的状态等于y的状态,那么就把x和y合并,也就是

p[find(a)] = find(b);
 p[find(a + Base)] = find(b + Base);

如果输入的字符串表示奇数,同上

当x的状态等于y的状态时矛盾

if (find(a) == find(b))
 {res = i - 1;break;
 }

否则,x的状态与y的反状态合并,x的反状态与y的状态合并,因为x与y是相反的,那么x就与y的反状态是一致的,对于y也一样

p[find(a + Base)] = find(b);
 p[find(a)] = find(b + Base);

最后的代码:

#pragma GCC optimize(2)
 #include 
 using namespace std;
 typedef long long ll;
 #define mm(a) memset(a, 0, sizeof(a))
 #define PI acos(-1)
 #define LLu unsigned long long
 #define PLL pair
 #define PII pair
 #define xx first 
 #define yy second 
 ll gcd(ll a, ll b) {return b ? gcd(b, a%b) : a; }
 ll lcm(ll a, ll b) {return a/gcd(a, b)*b;}
 const int INF = 0x3f3f3f3f;
 const int N = 4e4 + 10, Base = N/2;int n, m;
 int p[N];
 unordered_maps;int get(int x)
 {if(s.count(x) == 0) s[x] = ++n;return s[x];
 }
 int Find(int x)
 {if(x != p[x]){p[x] = Find(p[x]);}return p[x];
 }
 int main()
 {cin >> n >> m;n = 0;for(int i = 0; i <= N; i ++) p[i] = i;int res = m;for(int i = 1; i <= m; i ++){int x, y;string type;cin >> x >> y >> type;x = get(x - 1), y = get(y);if(type == "even"){if(Find(x + Base) == Find(y)){res = i - 1;break;}p[Find(x)] = Find(y);p[Find(x + Base)] = Find(y + Base);}else{if(Find(x) == Find(y)){res = i - 1;break;}p[Find(x)] = Find(y + Base);p[Find(x + Base)] = Find(y);}}cout << res << endl;return 0;
 }


标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。