P10687 True Liars 个人题解

news/2025/11/23 17:34:49/文章来源:https://www.cnblogs.com/bdxzay10180615/p/19261104

题目传送门

题目大意:

给你两个神圣种族和邪恶种族的人数以及询问次数,其中神圣种族的人说真话,邪恶种族的人说假话,请你判断那几个是神圣种族的人。

Solution:

题解区已经有很多带边权并查集的做法了,这里我用的是扩展域并查集。

首先我们来看,询问时是有逻辑的,分为三种情况:

  1. \(x\)\(y\) 是神圣种族时,因为当 \(x\) 为邪恶种族时会说假话,则 \(y\) 也是邪恶种族;当 \(x\) 为神圣种族时会说真话,则 \(y\) 也是神圣种族,所以此时 \(x\)\(y\) 同一种族。
  2. \(x\)\(y\) 是邪恶种族时,因为当 \(x\) 为邪恶种族时会说假话,则 \(y\) 是神圣种族;当 \(x\) 为神圣种族时会说真话,则 \(y\) 是邪恶种族,所以此时 \(x\)\(y\) 不是同一种族。
  3. \(x\)\(x\) 是神圣种族时,因为无论 \(x\) 是神圣种族还是邪恶种族,都会说自己是神圣种族的,此时该询问无意义,可以跳过。

然后我们根据结论对并查集设置两个域,第一个域为同族域(\(1\)\(p1+p2\)),第二个域为异族域(\(p1+p2+1\)\(2(p1+p2)\)),我们每读进来一个就把其同族异族关系通过并查集合并,然后得到每个人的同族和异族。然后我们需要确定到底那些是神圣种族哪些是邪恶种族,可以发现:第 \(i\) 个人所在的集合是神圣种族时,与 \(i+p1+p2\) 处于一个集合的都是邪恶种族,即 \(fa_{j}(1\le i\le p1+p2,j\neq i)=fa_{i}\) 的都是神圣种族,\(fa_{j}(p1+p2+1\le i\le 2(p1+p2),j\neq i+p1+p2)=fa_{i+p1+p2}\) 的都是邪恶种族,而当第 \(i\) 个人所在集合是邪恶种族时同理。所以这个集合是什么种族是由其 \(fa\) 时好时坏决定的,我们用一个 \(a\) 数组表示集合的 \(fa\) 是神圣种族时神圣种族人数,用 \(b\) 数组表示集合的 \(fa\) 是邪恶种族时神圣种族人数。通过这个我们可以用一个背包 DP 来解决神圣种族有哪些人。

首先我们定义 \(dp_{i,j}\) 表示满足前 \(i\) 个集合有 \(j\) 个好人的方法数,转移方程就为:
\(dp_{i,j}=dp_{i,j}+\begin{cases}dp_{i-1,j-a_{i}} & a_{i}\le j\\dp_{i-1,j-b_{i}} & b_{i}\le j\end{cases}\) 初始状态为 \(dp_{0,0}=1\) 表示没有人时也算一种方案,最终状态为 \(dp_{cnt,p1}=1\),其中 \(cnt\) 为划分的集合数,如果最终状态不等于 \(1\),那么有多种方案,此时无解;否则我们就可以回溯找 \(dp_{i,j}\) 在何时发生转移的,并标记下此时的神圣种族最后枚举一遍输出就行(此时保证字典序输出)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1500;
inline int read(){int x=0,f=1;char c=getchar();while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}return x*f;
}
int n,p1,p2,cnt,fa[N],dp[N][N],h[N],s[N],st[N],a[N],b[N];
//cnt表示集合数量,fa[i]表示i的祖先,dp[i][j]表示前i个集合有j个神圣种族的方法数
//h[i]是在按秩合并是判断大小所用的,s[i]表示i所在集合的大小,st[i]表示i所在的集合
//a[i]表示fa[i]是神圣种族时神圣种族人数,b[i]表示fa[i]是邪恶种族时神圣种族人数
inline int sfind(int x){if(x==fa[x])return x;return fa[x]=sfind(fa[x]);
}
inline void merge(int x,int y){//并查集->按秩合并 int fx=sfind(x),fy=sfind(y);if(fx==fy)return;if(h[fx]>h[fy])s[fx]+=s[fy],fa[fy]=fx;else if(h[fx]<h[fy])s[fy]+=s[fx],fa[fx]=fy;elses[fx]+=s[fy],fa[fy]=fx,h[fx]++;
}
int main(){while(n=read(),p1=read(),p2=read()){if(!n && !p1 && !p2)break;int m=p1+p2;for(int i=1;i<=m;i++){fa[i]=i,fa[i+m]=i+m;s[i]=h[i]=h[i+m]=1; s[i+m]=st[i]=st[i+m]=0;}for(int i=1;i<=n;i++){int x=read(),y=read();string c;cin>>c;if(c=="no")merge(x,y+m),merge(x+m,y);elsemerge(x,y),merge(x+m,y+m);}memset(dp,0,sizeof(dp));dp[0][0]=1;cnt=0;for(int i=1;i<=m;i++){int father=sfind(i);if(father!=i)continue;st[father]=st[father+m]=++cnt;a[cnt]=s[father];b[cnt]=s[father+m];}for(int i=1;i<=cnt;i++){//枚举集合 for(int j=min(a[i],b[i]);j<=p1;j++){//枚举神圣种族的人数 if(j>=a[i]) dp[i][j]+=dp[i-1][j-a[i]];if(j>=b[i])dp[i][j]+=dp[i-1][j-b[i]];}} if(dp[cnt][p1]!=1){//如果方案数不唯一退出 puts("no");continue;}for(int i=cnt;i;i--){//回溯寻找什么时候发生了转移 if(dp[i-1][p1-a[i]])p1-=a[i],a[i]=-1;if(dp[i-1][p1-b[i]])p1-=b[i],a[i]=-2;}for(int i=1;i<=m;i++){//枚举所有人找到神圣种族的人 int father=sfind(i);if(st[father]){if((father>m && a[st[father]]==-2) || (father<=m && a[st[father]]==-1))printf("%d\n",i);}	}puts("end");}return 0;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/974080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kali资料

Kali资料解决Kali Linux APT更新中的GPG签名错误问题

题解:Luogu P14522 【MX-S11-T3】空之碎物

题意 定义 \(\ominus\) 为二进制不退位减法。对于一个可重集 \(S\),你可以进行若干次操作,每次操作可以选择 \(S\) 中的两个数 \(x,y\),合并成 \(x\ominus y\) 或 \(y\ominus x\)。定义 \(f(S)\) 为将 \(S\) 合并至…

10分钟,无需公网 IP!零门槛搭建 NapCatQQ 趣味 AI 人机,聊天互动超简单

10分钟,无需公网 IP!零门槛搭建 NapCatQQ 趣味 AI 人机,聊天互动超简单无需公网 IP 即可打造 QQ 智能人机:核心依赖 NapCat(接收 QQ 消息)与 AstrBot(提供 AI 能力)容器,通过 WebSocket 建立连接,配置硅基流…

1088. Rational Arithmetic (20)

1088. Rational Arithmetic (20)#include <iostream>using namespace std;long long getsame(long long a, long long b) {if(b != 0){return getsame(b, a % b);}else{return a;} }void simplify(long long &am…

1087. All Roads Lead to Rome (30)

1087. All Roads Lead to Rome (30)#include <iostream> #include <vector> #include <string.h>using namespace std;struct node {int next, cost; };vector<node> v[27000]; vector<int…

解码UDP

UDP 协议基础认知 UDP(User Datagram Protocol,用户数据报协议)是传输层核心协议之一,基于 IP 协议实现跨网络主机进程间的无连接数据传输。它面向事务提供简单通信服务,不保证数据交付、有序性和重复防护,也不提…

人工智能之数据分析 numpy:第六章 数组基本操作

人工智能之数据分析 numpy:第六章 数组基本操作人工智能之数据分析 numpy 第六章 数组基本操作@目录人工智能之数据分析 numpy前言一、修改数组形状(Reshaping)1. reshape()2. resize()3. ravel() 与 flatten()二、…

2025中山办公场地租赁优选:中山西区金嘉创新港,一站式创业空间,赋能企业成长新机遇

随着中山市产业升级与创新创业浪潮的蓬勃发展,优质办公空间已成为企业发展的重要基石。在2025年中山商业地产市场中,中山西区金嘉创新港凭借多元化的空间解决方案、完善的配套服务体系及卓越的区位优势,成为各类企业…

国产数据库替代MongoDB:政务电子证照新选择 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

读书笔记《投资的未来》,估算收益率

比较IBM和新泽西标准石油两家公司,一个新兴的,受追捧的,一个传统的,但结果,是石油公司胜出。尽管两只股票的业绩都不错,但是1950~2003年,新泽西标准石油的投资者每年可以取得14.42%的年收益率,这比IBM提供的1…

使用代码查询快递信息的方法(与查询天气的方式雷同)

第一步:在标签中添加JS文件具体内容如下:第二步:写出大概的框架第三步:写JS部分(1)定义appkey和API地址(2)校验输入和显示加载状态(3)调用API和解析返回数据(4)展示拼接内容和判断内容是否正确第四步:保存并运行查…

1101. Quick Sort (25)

1101. Quick Sort (25)#include <iostream> #include <vector> #include <algorithm>using namespace std;int num[100010], low[100010], high[100010];int main() {int n;scanf("%d", &…

1100. Mars Numbers (20)

1100. Mars Numbers (20)#include <iostream> #include <string.h>using namespace std;char ch[2][13][5] = {"tret", "jan", "feb", "mar", "apr",…

解码网络编程基础

进程间通信方式 基础概念 程序是数据和指令的集合,运行时成为进程,操作系统会为其分配资源并记录参数。同一主机内进程通信可通过管道、信号、消息队列、信号量集、共享内存实现,这些方式依赖主机本地系统资源,无法…

C++的3种继承方式

C++的3种继承方式 在 C++ 中,继承方式(public、protected、private)决定了基类成员在派生类中的访问权限,以及派生类对象对基类成员的访问权限。正确选择继承方式是实现封装、复用和多态的关键。以下是三种继承方式…

1082. Read Number in Chinese (25)

1082. Read Number in Chinese (25)#include <iostream> #include <string.h>using namespace std;int first = 1;void setfirst() {if(first == 1){first = 0;}else{printf(" ");} }int main()…

1081. Rational Sum (20)

1081. Rational Sum (20)#include <iostream>using namespace std;long long getsame(long long a, long long b) {if(b != 0){return getsame(b, a % b);}else{return a;} }void simplify(long long &a, lo…

1067. Sort with Swap(0) (25)

1067. Sort with Swap(0) (25)#include <iostream>using namespace std;int index[100010], num[100010];int main() {int n;scanf("%d", &n);int i, count = 0;for(i = 0; i < n; i++){scanf(…

1066. Root of AVL Tree (25)

1066. Root of AVL Tree (25)#include <iostream> #include <stdlib.h>using namespace std;typedef struct node {int key, bf;struct node *lchild, *rchild; }*bnode;void rrotate(bnode *root) {bnode…

1070. Mooncake (25)

1070. Mooncake (25)#include <iostream> #include <algorithm>using namespace std;struct node {double amounts, prices, perprice; }mooncakes[1010];int cmp(node n1, node n2) {return n1.perprice …