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 ...
随机推荐
- centos7 RPM命令安装操作
RPM 安装操作 命令: rpm -i 需要安装的包文件名 举例如下: rpm -i example.rpm 安装 example.rpm 包: rpm -iv example.rpm 安装 exam ...
- [转]nginx简易教程
安装 nginx官网下载地址 发布版本分为 Linux 和 windows 版本. 也可以下载源码,编译后运行. 从源代码编译 Nginx 把源码解压缩之后,在终端里运行如下命令: $ ./confi ...
- Heartbeat基本介绍----HA / vmware HA FT
Heartbeat是High-Availability Linux Project (Linux下的高可用性项目)的产物,是一套提供防止业务主机因不可避免的意外性或计划性宕机问题的高可用性软件.Hea ...
- 11 Python Libraries You Might Not Know
11 Python Libraries You Might Not Know by Greg | January 20, 2015 There are tons of Python packages ...
- JAVA 设计模式之 原型模式详解
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式利用的是克隆的原理,创建新的对象,JDK提供的Cloneable 和JSON. ...
- Android Butterknife使用方法总结
原文链接:http://blog.csdn.net/donkor_/article/details/77879630 前言: ButterKnife是一个专注于Android系统的View注入框架,以 ...
- vue通过修改element-ui相关类的样式修改element-ui组件的样式
可以在App.vue中的style中修改element-ui的样式. .el-menu{ width:160px !important; } 注意:一定要在属性值后面加上 !important 使自己 ...
- 最近开始学习python,学习到了关于web的内容。
然而在win10中IIS发布CGI脚本的时候遇到了各种各样的问题. 如ISAPI和CGI限制,权限限制等等,一一的百度解决了,最后又出现了 HTTP 错误 502.2 - Bad Gateway Th ...
- ADS 下 flash 烧写程序原理及结构
本原理:在 windows 环境下借助 ADS 仿真器将在 SDRAM 中的一段存储区域中的数据写到 Nand flash 存 储空间中.烧写程序在纵向上分三层完成: 第一层: 主烧写函数(完成将在 ...
- Caffe系列4——基于Caffe的MNIST数据集训练与测试(手把手教你使用Lenet识别手写字体)
基于Caffe的MNIST数据集训练与测试 原创:转载请注明https://www.cnblogs.com/xiaoboge/p/10688926.html 摘要 在前面的博文中,我详细介绍了Caf ...