食物链(POJ 1182):

题意: 有 NNN 个动物,编号为 1−N1-N1−N,每个动物都是是A,B,CA,B,CA,B,C 中的一种,但是我们并不知道它们是哪一种。现在给出 KKK 句话,“1XY”“1 \ X \ Y”“1 X Y” 表示 XXX 和 YYY 是同类,“2XY”“2 \ X \ Y”“2 X Y” 表示 XXX 吃 YYY 。如果当前的话于前面的某些真的话冲突,那么这句话就是假话,问一共有多少句假话。

思路: 已知 A→B→C→AA\rightarrow B\rightarrow C\rightarrow AA→B→C→A,一个环形的状态。因此类似于一个模 333 剩余系,d[x]=0d[x] = 0d[x]=0 表示 xxx 与 rootrootroot 同类别,d[x]=1d[x] = 1d[x]=1 表示 xxx 吃 rootrootroot,d[x]=2d[x] = 2d[x]=2 表示 xxx 被 rootrootroot 吃。因此所有偏移量加减都在模 333 剩余系中进行,具体实现可以查看代码。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 5*1e4+100;int n,m,fa[N],d[N];int find(int x){if(x == fa[x]) return x;int root = find(fa[x]);d[x] = (d[fa[x]]+d[x])%3;return fa[x] = root;
}int main()
{scanf("%d%d",&n,&m);int ans = 0;rep(i,0,n) fa[i] = i, d[i] = 0;rep(i,1,m){int op,x,y;scanf("%d%d%d",&op,&x,&y);if(x > n || y > n){ans++;continue;}if(x == y && op == 2){ans++;continue;}int fx = find(x), fy = find(y);if(fx == fy){if(op == 1){if((d[x]-d[y]+3)%3!=0) ans++;}else{if((d[x]-d[y]+3)%3!=1) ans++;}}else{if(op == 1){fa[fx] = fy;d[fx] = (d[y]-d[x]+3)%3;}else{fa[fx] = fy;d[fx] = (d[y]-d[x]+3+1)%3;    }}}printf("%d\n",ans);return 0;
}

关押罪犯(NOIP 2010 / CODEVS 1069):

题意: 现在有 NNN 名罪犯,编号分别为 1−N1-N1−N。有 MMM 对罪犯之间存在仇恨关系,有具体的怨气值 ccc。现在需要将 NNN 名罪犯安排到两所监狱中,如果有仇恨的两名罪犯在同一监狱,则会发生冲突,事件影响力为其对应的怨气值。问如果分配罪犯,才能使发生的冲突中最大的影响值最小。输出答案即可。(N≤2∗104,M≤105)(N\leq 2*10^4,M\leq 10^5)(N≤2∗104,M≤105)

思路: 由于本题只需要求影响力最大的冲突,因此我们可以贪心地来解决这个问题,先按照怨气值从大到小将 MMM 对罪犯进行排序,然后建立一个带权并查集,d[x]=x→rootd[x] = x\rightarrow rootd[x]=x→root 表示 xxx 与 rootrootroot 是否在同一个监狱。d[x]=0d[x] = 0d[x]=0 表示 xxx 与 rootrootroot 在同一监狱,d[x]=1d[x] = 1d[x]=1 表示 xxx 与 rootrootroot 在不同监狱,因此所有的偏移量计算都在模 222 剩余系中进行,也可以转化为异或计算。

所以对于每一对罪犯,查看他们所属并查集,如果不在同一并查集则将两者合并,并将其偏移量设为 111。如果在同一并查集,则查看二者的偏移量,若偏移量为 000,则冲突不可避免,输出答案即可。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 2*1e4+1000;
const int M = 1e5+100;int n,m,fa[N],d[N];
struct Node{int a,b,c;bool operator < (Node x) const {return c > x.c;}
}t[M];int find(int x){if(x == fa[x]) return x;int root = find(fa[x]);d[x] = (d[x]+d[fa[x]]+2)%2;return fa[x] = root;
}int main()
{int ans = 0;scanf("%d%d",&n,&m);rep(i,1,m) scanf("%d%d%d",&t[i].a,&t[i].b,&t[i].c);sort(t+1,t+1+m);rep(i,0,n) fa[i] = i, d[i] = 0;rep(i,1,m){int x = t[i].a, y = t[i].b;int fx = find(x), fy = find(y);if(fx != fy){fa[fx] = fy;d[fx] = (1+d[y]-d[x]+2)%2;}else{int yt = (d[x]-d[y]+2)%2;if(!yt){ans = t[i].c;break;} }}printf("%d\n",ans);return 0;
}

Rochambeau(POJ 2912):

题意: NNN 个人在玩剪刀石头布的游戏,其中有一个人是裁判,其余人随机被分到了三个阵营,即剪刀、石头、布。现在有 MMM 轮游戏,x&lt;,&gt;,=yx &lt;\ ,&gt;\ ,= yx< ,> ,=y 表示 xxx 与 yyy 之间的关系,其中裁判可以自由变换阵营。问是否可以根据这 MMM 轮游戏,判断出谁是裁判,输出裁判是谁以及最早在哪一轮可以找到裁判。如果无法判断,则输出 CannotdetermineCan\ not\ determineCan not determine,如果游戏的情况不符合题意,则输出 ImpossibleImpossibleImpossible 。(N≤500,M≤2000)(N\leq 500,M\leq 2000)(N≤500,M≤2000)

思路: 一开始做这题的时候,的确有些懵,只能想到如何发现有人变换了阵营,但是不知道如何找到这个人,并且确定这种情况是唯一的。

所以我们可以发现直接考虑整个问题会非常困难,再加上此题数据范围很小,直接做的话复杂度肯定很少,太对不起这个数据范围了。因此我们考虑 N2N^2N2 做法,即枚举每个人为裁判。枚举 xxx 为裁判时,如果 xxx 恰好为裁判,则并查集合并时不会发生矛盾。我们统计有多少个人为裁判时,并查集合并会发生矛盾,记人数为 cntcntcnt,并且统计每个人为裁判时,发生矛盾的轮数 ririri 。

现在我们来考虑输出答案的所有情况。
1.1.1. 如果 cnt=n−1cnt = n-1cnt=n−1,则可以唯一确定裁判,并且最早发现该裁判的轮数为 max(ri)max(ri)max(ri)。
2.2.2. 如果 cnt=ncnt = ncnt=n,则输出 impossibleimpossibleimpossible。
3.3.3. 其余情况则为 CannotdetermineCan\ not\ determineCan not determine。

至于带权并查集的合并类似于食物链问题,是个模 333 剩余系中的加减问题。

ps:读输入的时候1<3,如果用字符串读,不要只读一个数字… 推荐读取方式如下。

scanf("%d%c%d")

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 1000;
const int M  = 2000+100;
int n,m,fa[N],d[N],vis[N],error[N],cnt;int find(int x){if(x == fa[x]) return x;int root = find(fa[x]);d[x] = (d[x]+d[fa[x]]+3)%3;return fa[x] = root;
}struct Query{int x,y,cc;
}q[M];int main()
{while(~scanf("%d%d",&n,&m)){memset(vis,0,sizeof vis);memset(error,0,sizeof error);cnt = 0;rep(i,1,m){int x,y; char cc;scanf("%d%c%d",&x,&cc,&y);q[i].x = x, q[i].y = y;if(cc == '=') q[i].cc = 0;else if(cc == '<') q[i].cc = 1;else q[i].cc = 2;}rep(i,0,n-1){rep(k,0,n) fa[k] = k, d[k] = 0;rep(j,1,m){int x = q[j].x, y = q[j].y, cc = q[j].cc;if(x == i || y == i) continue;int fx = find(x), fy = find(y);if(fx != fy){fa[fx] = fy;d[fx] = (d[y]-d[x]+cc+3)%3;}else{int ttp = (d[x]-d[y]+3)%3;if(ttp != cc){cnt++;     // 产生矛盾个数error[i] = j;vis[i] = 1;break;} }}}// impossible// not determine// determineif(cnt == (n-1)) { //determineint ans = 0, hm;rep(i,0,n-1){ans = max(ans,error[i]);if(!vis[i]) hm = i;} printf("Player %d can be determined to be the judge after %d lines\n",hm,ans);}else if(cnt == n){ // impossibleprintf("Impossible\n");}   else{  // not determineprintf("Can not determine\n");}}return 0;
}

True Liars(POJ 1417):

题意: 给出 p1p1p1 个天使,p2p2p2 个魔鬼,一共有 nnn 个问题。每个问题的格式为 xy(yesorno)x\ y\ (yes\ or\ no)x y (yes or no) 表示问 xxx yyy 是否为天使,如果 xxx 为天使,则会说真话,如果 xxx 为魔鬼,则会说假话。问根据这 nnn 个问题,是否可以确定哪些人为天使,如果可以确定,按编号大小输出天使编号。(n≤1000,p1,p2≤300)(n\leq 1000,\ \ p1,p2\leq 300)(n≤1000,  p1,p2≤300)

思路: 遇到这样的 yesyesyes ororor nonono 问题,显然我们需要对于可能出现的情况进行模拟。
1.1.1. 假如 xxx 为天使,yyy 为天使,则回答 yesyesyes 。
2.2.2. 假如 xxx 为天使,yyy 为魔鬼,则回答 nonono 。
3.3.3. 假如 xxx 为魔鬼,yyy 为天使,则回答 nonono 。
4.4.4. 假如 xxx 为魔鬼,yyy 为魔鬼,则回答 yesyesyes 。

可以发现,如果回答是 yesyesyes,则 xxx 与 yyy 属于同一类。如果回答 nonono,则 xxx 与 yyy 类别相反。因此一个模 222 剩余系的带权并查集合并就可以维护所有人之间的关系。

因此不难发现,处理完 nnn 个问题之后,我们会拥有若干个并查集,每个并查集中都会分为两批人,两批人类别不同。

因此问题变成了,从这若干个并查集中随机选一部分,使得最后选中的人数恰好为 p1p1p1,并且这种选择方式唯一,则我们可以确定哪些人为天使。因此本题就变成了一个类似背包的问题,dp[i][j]dp[i][j]dp[i][j] 表示前 iii 个并查集选取人数为 jjj 一共有多少种选择方案,pre[i][j]=xpre[i][j]=xpre[i][j]=x 表示 dp[i][j]dp[i][j]dp[i][j] 由 dp[i−1][x]dp[i-1][x]dp[i−1][x] 更新而来。到此,本题即可顺利解决。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 700;int n,p1,p2,fa[N],d[N],base[N][2],tot,dp[N][600],pre[N][600],vis[N];
map<int,int> mp;int find(int x)
{if(x == fa[x]) return x;int root = find(fa[x]);d[x] = (d[x]+d[fa[x]]+2)%2;return fa[x] = root;
}void solve()
{memset(base,0,sizeof base);memset(vis,0,sizeof vis);mp.clear();tot = 0;rep(i,1,p1+p2){fa[i] = find(i);if(mp.find(fa[i]) == mp.end()) mp[fa[i]] = ++tot;int pos = mp[fa[i]];base[pos][d[i]]++;   //0: 相同 1:不同}memset(dp,0,sizeof dp);memset(pre,0,sizeof pre);dp[1][base[1][1]]++;dp[1][base[1][0]]++;rep(i,2,tot){int minn = min(base[i][0],base[i][1]);rep(j,minn,p1){if(j >= base[i][0] && dp[i-1][j-base[i][0]] != 0)dp[i][j] += dp[i-1][j-base[i][0]], pre[i][j] = j-base[i][0];if(j >= base[i][1] && dp[i-1][j-base[i][1]] != 0)dp[i][j] += dp[i-1][j-base[i][1]], pre[i][j] = j-base[i][1];}}if(dp[tot][p1] != 1) printf("no\n");else{int xx = tot, yy = p1;while(xx > 1){if(pre[xx][yy] == yy-base[xx][0]) vis[xx] = 0, yy -= base[xx][0];else if(pre[xx][yy] == yy-base[xx][1]) vis[xx] = 1, yy -= base[xx][1];xx--;}if(xx == 1){if(base[1][0] == yy) vis[1] = 0;else vis[1] = 1;}rep(i,1,p1+p2){int pos = mp[fa[i]];int dd = d[i];if(vis[pos] == 1 && dd == 1) printf("%d\n",i);else if(vis[pos] == 0 && dd == 0) printf("%d\n",i);}printf("end\n");}
}int main()
{while(~scanf("%d%d%d",&n,&p1,&p2)){if((n+p1+p2) == 0) break; rep(i,0,p1+p2) fa[i] = i, d[i] = 0;rep(i,1,n){int x,y; char op[10];scanf("%d%d",&x,&y);scanf("%s",op);int fx = find(x), fy = find(y);// LOG2("x",x,"y",y);// LOG2("fx",fx,"fy",fy);if(fx != fy){fa[fx] = fy;if(op[0] == 'n') d[fx] = (2+1+d[y]-d[x])%2;else d[fx] = (d[y]-d[x]+2)%2;}}solve();}return 0;
}

【带权并查集题目汇总】相关推荐

  1. hdu4829 带权并查集(题目不错)

    题意: Information Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...

  2. POJ1988(带权并查集,搬砖块)

    题意:        可以这样理解,有n快方形积木,一开始都是单独的放到哪,然后有两种操作 1 M a b 把a所在的那一堆落到b所在那一堆的上面(一开始自己是一堆) 2 C a 问a下面有多少个积木 ...

  3. hdu3234 带权并查集(XOR)

    题意:       给你n个未知的正整数,有三总操作       I P V            P的值是V       I P Q V          P XOR Q = V       Q K ...

  4. cdoj 1070 秋实大哥打游戏 带权并查集

    题目链接: http://acm.uestc.edu.cn/#/problem/show/1070 题意: 题解: 带权并查集 每次往上更新的时候,顺便把边权更新了就好 记住得路径压缩 代码: 1 # ...

  5. 2015 UESTC 数据结构专题H题 秋实大哥打游戏 带权并查集

    秋实大哥打游戏 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/59 Descr ...

  6. 2020CCPC(长春) - Ragdoll(启发式合并+带权并查集)

    题目大意:初始时给出 n 个集合,每个集合中都包含有一个数字,现在要求执行 m 次操作,每次操作分为下列三种类型: 1 x y:在 x 位置新建一个集合,并且放置一个数字 y 2 x y:合并集合 x ...

  7. How Many Answers Are Wrong HDU - 3038(带权并查集)

    TT and FF are - friends. Uh- very very good friends -________-b FF is a bad boy, he is always wooing ...

  8. 【POJ - 1703】Find them, Catch them(带权并查集之--种类并查集 权为与父节点关系)

    题干: Find them, Catch them Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 36176   Accep ...

  9. BZOJ 3362 Navigation Nightmare 带权并查集

    题目大意:给定一些点之间的位置关系,求两个点之间的曼哈顿距离 此题土豪题.只是POJ也有一道相同的题,能够刷一下 别被题目坑到了,这题不强制在线.把询问离线处理就可以 然后就是带权并查集的问题了.. ...

  10. 2017 西安网络赛A Tree(树上静态查询,带权并查集,矩阵乘法压位,好题)

    题目链接 题意: 给出 \(n(n \leq 3000)\) 个结点的一棵树,树上每个结点有一个 \(64 \times 64\) 的 \(0,1\)矩阵,每个结点上的矩阵是根据输入的 \(seed\ ...

最新文章

  1. java combinationsum_Leecode39 combination-sum
  2. git 文件全部标红_git冲突解决,代码冲突、合并冲突。【IDEA版本】
  3. C++中public,protected,private派生类继承问题和访问权限问题
  4. BZOJ 1005: [HNOI2008]明明的烦恼
  5. elasticsearch删除索引_一文带您了解 Elasticsearch 中,如何进行索引管理(图文教程)
  6. mysql应用层透明扩展_MySQL高扩展和高可用
  7. (二十五)深度学习目标检测:RCNN
  8. Chapter 5. MPEG-4 Visual
  9. Windows Phone开发(33):路径之其它Geometry 转:http://blog.csdn.net/tcjiaan/article/details/7483835...
  10. import sys是什么意思_学了半天,import 到底在干啥?
  11. xpath获取标签的属性值_爬虫必备技能之网页解析库:xpath用法和实战
  12. JAVA JDK API(中文) 1.6、1.8
  13. 压力测试 - Apache JMeter使用教程
  14. mysql 建数据库命令_新手入门MySQL数据库命令大全
  15. 光纤交换机配置zone
  16. 钉钉小程序的坑 么有开启通讯录权限,导致后台报错“没有调用该接口的权限”
  17. webView.addJavascriptInterface 用法
  18. matlab中删除照片_matlab中删除对象
  19. 面试必问之JVM原理 1
  20. 3D打印机的故障检测及排除

热门文章

  1. 关于spfile的一个难题
  2. 计算机审计初级难度,到现在你还不知道注会各科通过率 就真的out啦
  3. Wordpress如何正确书写Robots.txt
  4. ECharts项目小结~
  5. Oracle中登录OEM口令忘记,oracle oem创建过程 一直提示sys密码错误
  6. git配置ssh私钥_GitLab 配置
  7. c++项目实例_.NET Core CLI来启动应用程序的多个实例
  8. linux异步事件框架,基于Cortex-M系列CPU的异步事件驱动中间件
  9. 整数划分之四 【区间dp】讲解于思考方法
  10. 试看5分钟视频python_清华学姐推荐的Python视频400集,拿走不谢!