kuangbin带我飞QAQ 并查集
1. POJ 2236
给出N个点,一开始图是空白的,两个操作,一个是增加一个点(给出坐标),一个是查询两个点间是否相通,当两点间的距离小于D或者两点通过其他点间接相连时说这两个点相通。并查集维护,每次增加一个点加入到vector中并于之前的点比较,距离小于D则合并到一个集合即可。
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = ;
const int maxn = ; int N, D;
vector<int> alive;
bool vis[maxk];
int root[maxk];
int xi[maxk], yi[maxk]; int find( int x )
{ return x==root[x] ? x : root[x]=find(root[x]); } void join( int x, int y )
{
x = find(x); y = find(y);
if ( x == y )
return;
root[x] = y;
} int getDist( int i, int j )
{ return (xi[i]-xi[j])*(xi[i]-xi[j]) + (yi[i]-yi[j])*(yi[i]-yi[j]) ; } void init()
{
alive.clear();
memset(vis, , sizeof(vis) ); for ( int i = ; i < maxk; i++ )
root[i] = i; for ( int i = ; i <= N; i++ )
scanf( "%d %d", &xi[i], &yi[i] );
} int main()
{
freopen("F:\\cpp\\test.txt", "r", stdin );
scanf("%d %d", &N, &D);
init(); int x, y;
char str[];
while ( ~scanf("%s", str) )
{
if ( str[] == 'O' )
{
scanf("%d", &x);
vis[x] = true;
alive.push_back(x); for ( int i = ; i < alive.size()-; i++ )
if ( getDist(alive[i], x) <= D*D )
join(alive[i], x);
}
else
{
scanf("%d %d", &x, &y);
if ( (!vis[x]) || (!vis[y]) )
printf("FAIL\n");
else
{
x = find(x); y = find(y);
if ( x == y )
printf("SUCCESS\n");
else
printf("FAIL\n");
}
}
}
}
2. HDU 1213
并查集裸题,给N个点和M个关系,问最终有多少个集合
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = ;
const int maxn = ; int N, M;
bool vis[maxk];
int root[maxk]; int find( int x )
{ return x==root[x] ? x : root[x]=find(root[x]); } void join( int x, int y )
{
x = find(x); y = find(y);
if ( x == y )
return;
root[x] = y;
} void init()
{
memset( vis, , sizeof(vis) );
for ( int i = ; i < maxk; i++ )
root[i] = i;
} int main()
{
//freopen("F:\\cpp\\test.txt", "r", stdin ); int T; cin >> T;
while (T--)
{
int cnt = ;
int x, y; init();
scanf("%d %d", &N, &M); for ( int i = ; i <= M; i++ )
{
scanf("%d %d", &x, &y);
join(x,y);
} for ( int i = ; i <= N; i++ )
vis[find(i)] = true;
for ( int i = ; i <= N; i++ )
if (vis[i])
cnt++;
printf("%d\n", cnt);
} return ;
}
3.HDU 3038
和一道经典的题目(就是下一道)食物链很像,记得某场比赛做过类似的,当时以为是线段树各种做不出,,,结果发现原来是并查集
给一组数列,数字未知,给出A,B和[A,B]表示A到B这段子数列内的数字和,这样按顺序给出来,问你有多少个逻辑错误,遇到错误则跳过该错误。
比如给了[1,4] = 5, [1,2] = 3,这个时候再给一个[3,4]=6,那么这一行明显和前面得到的条件矛盾,所以错误数加一并且跳过这一行,具体是用向量操作,可以参考这两篇博客:
https://www.cnblogs.com/liyinggang/p/5327055.html
https://blog.csdn.net/niushuai666/article/details/6981689
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = ;
const int maxn = 2e5+; int root[maxn];
int sum[maxn]; int _find( int x )
{
int fa = root[x]; if ( x == root[x] )
return x; root[x] = _find(fa);
sum[x] += sum[fa]; return root[x];
} void init( int n )
{
for ( int i = ; i <= n; i++ )
{
root[i] = i;
sum[i] = ;
}
} int main()
{
//freopen("F:\\cpp\\test.txt", "r", stdin ); int N, M, cnt;
while (cin >> N >> M)
{
init(N); cnt = ;
int x, y, val;
int xr, yr;
while ( M-- )
{
scanf("%d %d %d", &x, &y, &val); x--;
xr = _find(x); yr = _find(y);
if ( xr == yr )
{
if ( val != sum[x] - sum[y] )
cnt++;
}
else
{
root[xr] = yr;
sum[xr] = sum[y]-sum[x]+val;
}
} printf("%d\n", cnt);
} return ;
}
4. HDU 1272
POJ日常抽风,只能跑来做HDU的题目了233,实际上就是给定一个图让你判断是不是一颗连通树,所以有两个条件:无环,连通。无环用并查集判断,如果有两个点都在同一并查集,则说明肯定有环了。连通可以用(在无环的情况下)点数=边数+1判断,也可以用最后算出来总共只有一个集合来判断。坑爹的是要考虑空树,也就是直接给0 0的情况...
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <set>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = ;
const int maxn = 1e5+; int root[maxn];
set<int> num; int _find( int x )
{ return x==root[x] ? x : root[x]=_find(root[x]); } void merge( int x, int y )
{
x = _find(x); y = _find(y);
if ( x == y )
return;
root[x] = y;
} void init()
{
num.clear();
for ( int i = ; i < maxn; i++ )
root[i] = i;
} int main()
{
//freopen("F:\\cpp\\test.txt", "r", stdin ); int x, y;
while ()
{
int cnt = ;
bool ok = true;
init(); scanf("%d %d", &x, &y);
if ( x == - )
break;
if ( x == )
{
printf("Yes\n");
continue;
}
merge(x,y);
num.insert(x); num.insert(y);
cnt++; while ()
{
scanf("%d %d", &x, &y);
if ( x == )
break;
num.insert(x); num.insert(y);
cnt++; if (ok)
{
x = _find(x); y = _find(y);
if ( x == y )
ok = false; merge(x, y);
}
} if ( ok && num.size() == cnt+ )
printf("Yes\n");
else
printf("No\n");
}
return ;
}
5. ZOJ 3261
思路简单,写起来麻烦...大意是给N个点M条边,每个点有一个power值。然后有两个操作,一是毁灭其中一条边,二是询问一个点A,求与A直接或间接相连的所有点中power值最大的那个,且求出来的power值需大于A的power值,当有多个点power值相同,取序号最小那个。
显然并查集不支持删除,所以需要逆向操作,先按顺序存储所有操作,做出删完所有该删的边的最终图,再从后往前加边,询问。
询问操作比较好存,毁灭操作就难一点了,这里我用了map,因为map搜索是log级别的,即使要搜两边(x y和y x)也很快。其他除了并查集merge的时候要注意点就没什么了,就是写的好慢...
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = ;
const int maxn = 1e4+; //struct edge {
// int to, next;
//}e[4*maxn]; map<pair<int,int>,bool> mmap;
map<pair<int,int>,bool>::iterator itor; int xi[*maxn], yi[*maxn], query[*maxn];
int root[maxn], val[maxn];
int N, M, Q, death_num; //void addedge( int u, int v )
//{ e[cnt].to = v; e[cnt].next = linjie[u]; linjie[u] = cnt++; } int _find( int x )
{ return x==root[x] ? x : root[x]=_find(root[x]); } void merge( int x, int y )
{
x = _find(x); y = _find(y);
if ( x == y )
return; //让 x < y
if ( x > y ) swap(x,y); if ( val[x] > val[y] ) root[y] = x;
else if ( val[x] < val[y] ) root[x] = y;
else root[y] = x;
} void init( int n )
{
death_num = ;
mmap.clear(); //memset( linjie, -1, sizeof(linjie) );
for ( int i = ; i <= n; i++ )
root[i] = i;
} void read( int n )
{
//读入power
for ( int i = ; i < n; i++ )
scanf("%d", &val[i]); //读入边
cin >> M;
int x, y;
for ( int i = ; i < M; i++ )
{
scanf("%d %d", &x, &y);
mmap.insert( make_pair(make_pair(x,y), true) );
} //读入query
cin >> Q;
char str[];
for ( int i = ; i < Q; i++ )
{
scanf("%s", str);
if ( str[] == 'q' )
{
scanf("%d", &x);
query[i] = x;
}
else
{
//依次读入被毁灭的边,然后在mmap中寻找并变为false
scanf("%d %d", &xi[death_num], &yi[death_num]);
itor = mmap.find(make_pair(xi[death_num],yi[death_num]));
if ( itor != mmap.end() )
(*itor).second = false;
else
{
itor = mmap.find(make_pair(yi[death_num],xi[death_num]));
(*itor).second = false;
} death_num++;
query[i] = -;
}
}
} void make_map()
{
int x, y; //如果该边没有被毁灭
for ( itor = mmap.begin(); itor != mmap.end(); itor++ )
if ( (*itor).second )
{
x = ((*itor).first).first; y = ((*itor).first).second;
//addedge( x, y );
//addedge( y, x );
merge( x, y );
}
} void solve()
{
int tmp;
for ( int i = Q-; i >= ; i-- )
{ if ( query[i] != - ) //如果是询问
{
tmp = _find(query[i]);
if ( val[tmp] <= val[query[i]] )
query[i] = -;
else
query[i] = tmp;
}
else
{
death_num--;
merge(xi[death_num], yi[death_num]);
}
}
} int main()
{
//freopen("F:\\cpp\\test.txt", "r", stdin ); int cnt = ;
while ( ~scanf("%d", &N) )
{
if ( cnt )
printf("\n");
init(N);
read(N);
make_map();
solve();
for ( int i = ; i < Q; i++ )
if ( query[i] != -)
printf("%d\n", query[i]);
cnt++;
} return ;
}
6. POJ 2492
并查集做这种题实在太好了,这道题像是食物链的弱化版,给N个虫子,只有异性间才会交配.....给M个交配方案,看看虫子里面有没有同性恋(笑)这个关系还是比较简单的,用0表示两虫子同性,1表示虫子异性,rela[i]表示i和根节点的关系。
三个虫子中有三种关系(A-B, B-C, C-A)只要知道其中两种,很容易推出第三种,然后推出的关系维护并查集,具体看代码,很简单
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = 1e6+;
const int maxn = ; //rela[i]表示i与其根的关系 为0表示同性,为1表示异性
int root[maxn];
bool rela[maxn];
int N, Q; void init()
{
scanf("%d %d", &N, &Q); memset( rela, , sizeof(rela) );
for ( int i = ; i <= N; i++ )
root[i] = i;
} bool getRela( bool master, bool minor )
{
//如果两者关系为异性,则第三条关系相反
if ( master )
return (!minor);
else
return minor;
} int _find( int x )
{
int r = x, pre = root[x];
if ( r == root[r] )
return root[r]; r = _find(root[r]);
root[x] = r;
rela[x] = getRela( rela[x], rela[pre] ); return root[x];
} void merge( int x, int y, int xr, int yr )
{
//tmp是y和xr的关系
bool tmprela = getRela( true, rela[x] );
root[xr] = yr;
rela[xr] = getRela( tmprela, rela[y] );
} int main()
{
//freopen("F:\\cpp\\test.txt", "r", stdin ); int T; cin >> T;
int CNT = ;
while (T--)
{
bool nobug = true;
int x, y, xr, yr; init();
while (Q--)
{
scanf("%d %d", &x, &y);
if (nobug)
{
xr = _find(x); yr = _find(y);
if ( xr == yr )
{
if ( rela[x] != getRela(true, rela[y]) )
nobug = false;
}
else
merge(x, y, xr, yr);
}
}
printf("Scenario #%d:\n", CNT++);
if ( nobug )
printf("No suspicious bugs found!\n");
else
printf("Suspicious bugs found!\n");
printf("\n");
} return ;
}
7.POJ 1182
食物链,经典题目,有了前面的铺垫这道题就算是硬算也不难,前面几题集合中只有两种关系,这里就有三种关系,本质上是向量+偏移量,懂了还是不难的
PS:这道题只能单次输入,如果加了while循环输入就会WA...完全不知道为什么,害我查了一个小时的bug......
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = 1e6+;
const int maxn = 5e4+; int root[maxn], rela[maxn]; int _find( int x )
{
if ( x == root[x] )
return x; int pre = root[x];
root[x] = _find(root[x]);
rela[x] = (rela[x] + rela[pre]) % ; return root[x];
} int main()
{
//freopen("F:\\cpp\\test.txt", "r", stdin );
int N, K, cnt;
scanf("%d %d", &N, &K);
for ( int i = ; i <= N; i++ )
root[i] = i;
cnt = ;
memset( rela, , sizeof(rela) ); int d, x, y, xr, yr;
while (K--)
{
scanf("%d %d %d", &d, &x, &y);
d--; if ( x > N || y > N )
{ cnt++; continue; }
if ( x == y && d == )
{ cnt++; continue; } xr = _find(x); yr = _find(y);
if ( xr == yr )
{
if ( d != ((rela[x] - rela[y] + )%) )
cnt++;
}
else
{
root[xr] = yr;
rela[xr] = (rela[y] + d - rela[x] + ) % ;
}
}
printf("%d\n", cnt); return ;
}
8.POJ 1611 The Suspects
并查集裸题,给M个组,每个组有N个学生,其中0号学生是感染者,和感染者同一组的都是感染者,所以只要求0号学生为根的集合的学生个数即可,merge时约定0号学生为根就可以了。
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <math.h>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = 3e4+;
const int maxn = 5e4+; int root[maxk], num[maxk];
int N, M; void init()
{
for ( int i = ; i <= N; i++ )
{
root[i] = i;
num[i] = ;
}
} int _find(int x)
{ return x==root[x] ? x : root[x] = _find(root[x]); } void merge( int x, int y )
{
int xr = _find(x);
int yr = _find(y); if (xr != yr)
{
if (xr == )
{
root[yr] = xr;
num[xr] = num[xr] + num[yr];
}
else
{
root[xr] = yr;
num[yr] = num[xr]+num[yr];
}
}
} int main()
{
while ( ~scanf("%d %d", &N, &M) )
{
if ( N == && M == )
break; init(); int x;
for ( int j = ; j < M; j++ )
{
int a, b; scanf("%d", &x);
scanf("%d", &a);
if ( x != )
for (int i = ; i <= x-; i++)
{
scanf("%d", &b);
merge(a, b);
a = b; }
} printf("%d\n", num[]);
} return ;
}
9. POJ 1417 True Liars
村子里面有p1个好人p2个坏人,好人只会说真话,坏人只会说假话,现在可以问N个问题, 每个问题就是随机挑两人a和b,让a回答b是不是好人,然后让你判断出所有的好人。
如果这道题只是求有没有矛盾,那就是一道很简单的带权并查集,和前面那个同性恋虫子思路一样。但现在问题是要求出所有好人,显然只根据问的N个问题是求不出来的,因为如果没有第一个能被确定为好人或者坏人的人,那么剩下所有人都是一个相对的关系,这样只能推断两个人是同为好人还是同为坏人或者一好一坏。经过简单的推算发现回答为yes时a和b为同类人,no时a与b异类,开个rela[i]数组表示i与根节点是同类还是异类, 这样带权并查集直接走起。
那么我们可以用并查集先求出几个大集合,并且记录每个大集合A类人和B类人的个数,当然我们现在并不知道A类是好人还是坏人,只是一个相对的关系。显而易见我们应该在每个大集合各中抽取一个A类人或者B类人组成好人集合,如果好人集合人数正好为p1,并且这种选法唯一那我们就能确定所有好人了。比如只有一个大集合有4个A类人和4个B类人,而好人只有4个,那我们就不知道到底是A类是好人还是B类是好人了。实际上就像一个组合问题,看能否形成唯一的组合使其总数正好为p1,这点用dp很简单就能实现,dp[i][j]表示第i个大集合中使人数为j的方法最多有多少种,最后只要看dp[cnt][p1]是否等于1就可以了。
然而这题最麻烦的还是记录路径,有一种方法是如果从dp[cnt][p1]往前推。因为dp[i][j]必然是由dp[i-1][j-A]和dp[i-1][j-B](A, B分别为某个大集合A类人和B类人的个数)推过来的,这两个dp只有一个为1,不然dp[i][j]就等于2了(有两种路径可以使好人人数达到 j ),所以只要将p1一步一步减回去看dp为1的那一类就是好人类。
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = 3e4+;
const int maxn = ; int n, p1, p2, cnt;
//num[i][0]表示该集合中与根节点i种类相同的人个数,反正亦然
int num[][], root[];
//dp[i][j]表示前i的个大集合中天使人数为j的集合最多多少个
int dp[][];
int divide[][];
bool rela[]; //false表示与根为同类 vector<int> ans, _list;
vector<int> v0[], v1[]; void init()
{
cnt = ; ans.clear(); _list.clear();
memset(num, , sizeof(num));
memset(rela, , sizeof(rela));
memset(dp, , sizeof(dp));
for ( int i = ; i <= p1+p2; i++ )
{
root[i] = i;
num[i][] = ;
}
} int _find( int x )
{
if (x == root[x])
return root[x]; int pre = _find(root[x]); if (rela[root[x]])
rela[x] = !rela[x];
root[x] = pre; return pre;
} void merge( int x, int y, bool link )
{
int xr = _find(x), yr = _find(y);
if (xr == yr)
return; if (rela[x])
link = !link;
if (rela[y])
rela[xr] = !link;
else
rela[xr] = link; root[xr] = yr; //如果xr与yr是同类,则yr组中同类的数目要加上xr组中与xr同类的个数
if (!rela[xr])
{
num[yr][] += num[xr][];
num[yr][] += num[xr][];
}
else
{
num[yr][] += num[xr][];
num[yr][] += num[xr][];
}
} void solve()
{
_list.push_back();
for ( int i = ; i <= p1+p2; i++ )
{
if ( _find(i) == i )
{
divide[cnt][] = num[i][];
divide[cnt][] = num[i][];
_list.push_back(i);
cnt++;
}
} for ( int i = ; i <= p1+p2; i++ )
{
int rt = root[i];
if ( !rela[i] )
v0[rt].push_back(i);
else
v1[rt].push_back(i);
} //处理DP
dp[][] = ;
for (int i = ; i < cnt; i++)
{
int mmin = Min(divide[i][], divide[i][]);
for (int j = p1; j >= mmin; j--)
{
dp[i][j] += dp[i-][j-divide[i][]];
dp[i][j] += dp[i-][j-divide[i][]];
}
} if ( dp[cnt-][p1] == )
{
int tmp1 = p1, tmp2 = p2;
for ( int i = cnt-; i >= ; i-- )
{
if ( tmp1-divide[i][]>= && tmp2-divide[i][]>= && dp[i-][tmp1-divide[i][]]== )
{
int size = v0[_list[i]].size();
for ( int j = ; j < size; j++ )
ans.push_back(v0[_list[i]][j]);
tmp1 -= divide[i][];
tmp2 -= divide[i][];
}
else if ( tmp1-divide[i][]>= && tmp2-divide[i][]>= && dp[i-][tmp1-divide[i][]]== )
{
int size = v1[_list[i]].size();
for ( int j = ; j < size; j++ )
ans.push_back(v1[_list[i]][j]);
tmp1 -= divide[i][];
tmp2 -= divide[i][];
} v0[_list[i]].clear();
v1[_list[i]].clear();
}
sort(ans.begin(), ans.end());
for ( int i = ; i < ans.size(); i++ )
printf("%d\n", ans[i]);
printf("end\n");
}
else
{
printf("no\n");
for ( int i = ; i < _list.size(); i++ )
{
v0[_list[i]].clear();
v1[_list[i]].clear();
}
}
} int main()
{
while ( ~scanf("%d %d %d", &n, &p1, &p2) && n+p1+p2 )
{
init();
int x, y;
char str[];
bool link = false; //false表示x与y是同类 while (n--)
{
scanf("%d %d", &x, &y); scanf("%s", str);
if (str[] == 'n') link = true;
else link = false; merge(x, y, link);
} solve();
} return ;
}
10. POJ 1733
看上去是一道简单的种类并查集,但是关系并不好找,在网上看到一大神的思路,遂跪。
对于[l,r],我们可以简单的设sum[l-1],sum[r]为0~l-1,0~r的前缀,这和前面种类并查集是一样的,然而这道题只给了你[l,r]之间1的个数为奇数或者偶数,这我半天都没反应过来怎么转换。其实可以这样想,如果[l,r]之间1个数为奇数,那么sum[l-1]^sum[r]就应该等于1,因为可以把sum[r]拆为0~l-1和l~r这两端,前面一段和sum[l-1]异或就变成零了,后面一段因为有奇数个1所以异或结果为1,同理如果[l,r]间1为偶数,那么最后就应该等于0。但这道题我们并不需要存sum,我们只要一个rela数组存奇偶关系, 所以每条向量(假设向量两个端点为x,y)的rela值就应该等于sum[x]^sum[y]。令三角向量三个端点为l-1, r, root,稍微推一下就知道怎么写了。
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxn = 2e4+; //1个数为偶数则为0
int rela[maxn];
int root[maxn], num[maxn];
int len, N, cnt, n; struct node {
int lhs, rhs, rela;
}lisan[maxn]; int _find(int x)
{
if ( x == root[x] ) return root[x];
int pre = _find(root[x]);
rela[x] = rela[x]^rela[root[x]];
root[x] = pre;
return root[x];
} void init()
{
cnt = ;
memset(rela, , sizeof(rela));
} void read()
{
scanf("%d", &N);
init(); int x, y; char str[];
for (int i = ; i <= N; i++ )
{
scanf("%d %d %s", &lisan[i].lhs, &lisan[i].rhs, str );
lisan[i].rela = str[]=='e' ? : ;
num[++cnt] = lisan[i].lhs-; num[++cnt] = lisan[i].rhs;
} sort(num+, num++cnt);
n = unique(num+, num++cnt)-num-;
} int main()
{
while (~scanf("%d", &len))
{
read(); for ( int i = ; i <= n; i++ )
root[i] = i; int x, y, xr, yr;
for ( int i = ; i <= N; i++ )
{
x = lower_bound(num+, num++n, lisan[i].lhs-)-num;
y = lower_bound(num+, num++n, lisan[i].rhs)-num; xr = _find(x); yr = _find(y);
if ( xr == yr )
{
if ( rela[x]^rela[y] != lisan[i].rela )
{ printf("%d\n", i-); return ; }
}
else
{
root[xr] = yr;
rela[xr] = rela[x]^rela[y]^lisan[i].rela;
}
}
printf("%d", N);
} return ;
}
kuangbin带我飞QAQ 并查集的更多相关文章
- kuangbin带我飞QAQ 线段树
1. HDU1166 裸线段树点修改 #include <iostream> #include <string.h> #include <cstdio> #incl ...
- kuangbin带我飞QAQ DLX之一脸懵逼
1. hust 1017 DLX精确覆盖 模板题 勉强写了注释,但还是一脸懵逼,感觉插入方式明显有问题但又不知道哪里不对而且好像能得出正确结果真是奇了怪了 #include <iostream& ...
- kuangbin带我飞QAQ 最短路
1. poj 1502 Mathches Game 裸最短路 #include <iostream> #include <string.h> #include <cstd ...
- 洛谷P2661 信息传递 [NOIP2015] 并查集/乱搞 (待补充!
感觉我好水啊,,,做个noip往年题目还天天只想做最简单的,,,实在太菜辽 然后最水的题目还不会正解整天想着乱搞,,, 虽然也搞出来辽233333 好滴不扯辽赶紧写完去做紫题QAQ 正解:并查集 ...
- 谈一谈并查集QAQ(上)
最近几日理了理学过的很多oi知识...发现不知不觉就有很多的知识忘记了... 在聊聊并查集的时候顺便当作巩固吧.... 什么是并查集呢? ( Union Find Set ) 是一种用于处理分离集合的 ...
- [ An Ac a Day ^_^ ] [kuangbin带你飞]专题五 并查集 POJ 2236 Wireless Network
题意: 一次地震震坏了所有网点 现在开始修复它们 有N个点 距离为d的网点可以进行通信 O p 代表p点已经修复 S p q 代表询问p q之间是否能够通信 思路: 基础并查集 每次修复一个点重新 ...
- kuangbin带你飞 并查集 题解
做这套题之前一直以为并查集是很简单的数据结构. 做了才发现自己理解太不深刻.只看重片面的合并集合.. 重要的时发现每个集合的点与这个根的关系,这个关系可以做太多事情了. 题解: POJ 2236 Wi ...
- kuangbin 并查集
A : Wireless Network POJ - 2236 题意:并查集,可以有查询和修复操作 题解:并查集 #include<iostream> #include<cstdi ...
- [kuangbin带你飞]专题五 并查集
并查集的介绍可以看下https://www.cnblogs.com/jkzr/p/10290488.html A - Wireless Network POJ - 2236 An earthquake ...
随机推荐
- leetcood学习笔记-79-单词搜索
题目描述: 方法一;回溯 class Solution: def exist(self, board: List[List[str]], word: str) -> bool: max_x,ma ...
- SQL Server - Store procedure 如何返回值
存储过程 返回值 procedure return values : http://www.cnblogs.com/SunnyZhu/p/5542347.html return.select.outp ...
- matplotlib 画图颜色参数值及对应色卡
matplotlib 色卡对应参数值 cnames = { 'aliceblue': '#F0F8FF', 'antiquewhite': '#FAEBD7', 'aqua': '#00FFFF', ...
- Docker保存日志到本地
其实很简单 docker logs +你需要添加的额外参数 + 容器id >文件名称 然后查看这个文件就可以了,也可以通过ftp协议下载到本地
- 【未完成】Jmeter接口自动化测试:参数化设置
1. 从CSV文件读取参数 创建一个CVS文件,文件第一行不写参数名,直接从参数值开始,每一列代表一个参数 在测试计划或者线程组中,添加一个配置元件-->CSV 数据文件设置 Filename: ...
- Codeforces-GYM101873 G Water Testing 皮克定理
题意: 给定一个多边形,这个多边形的点都在格点上,问你这个多边形里面包含了几个格点. 题解: 对于格点多边形有一个非常有趣的定理: 多边形的面积S,内部的格点数a和边界上的格点数b,满足如下结论: 2 ...
- 深入浅出写一个多级异步回调从基础到Promise实现的Demo
今天一时兴起,写了一个渐进升级的异步调用demo,记录一下. 1. 最基础的同步调用 //需求:f2在f1之后执行,且依赖f1的返回值.如下: function f1(){ var s="1 ...
- python 对excel操作
在python中,对excel表格读,写,追加数据,用以下三个模块:1.wlrd 读取excel表中的数据2.xlwt 创建一个全新的excel文件,然后对这个文件进行写入内容以及保存.3.xluti ...
- uoj#311 【UNR #2】积劳成疾
题目 考虑直接顺着从\(1\)填数填到\(n\)发现这是在胡扯 所以考虑一些奇诡的东西,譬如最后的答案长什么样子 显然某一种方案的贡献是一个\(\prod_{i=1}^nw_i^{t_i}\)状物,\ ...
- POJ 2074 /// 判断直线与线段相交 视野盲区
题目大意: 将所有物体抽象成一段横向的线段 给定房子的位置和人行道的位置 接下来给定n个障碍物的位置 位置信息为(x1,x2,y) 即x1-x2的线段 y相同因为是横向的 求最长的能看到整个房子的一段 ...