测试地址:Mafia
做法: 本题需要用到环套树DP。
按照题目构图,很显然是我们很熟悉的环套树森林。接下来我们进行分析,最后活下来一些什么人是合法的呢?观察发现,一个人的目标如果是自己那就必死,而没有被作为目标的人一定存活,还有一个特别重要的性质:一个人iii和他的目标aia_iai​到最后不可能都存活。在满足这些条件的情况下,我们一定可以构造出一个顺序使得最后存活特定的人。于是问题就非常好分析了。
首先来看怎么样死亡最多,也就是存活最少。由于上面我们知道,没有被作为目标的人一定存活,那么其他的人是一定都会死吗?不一定,因为我们再次发现,一个连通块内至少会有一个人存活。因此我们对所有没被作为目标的人打个标记,在进行连通块的搜索的时候,如果当前连通块内不存在这样的人(事实上,这种情况只有可能是一个环),那么就会多出一个人存活。当然,如果这个环内只有一个人,那么因为这个人目标是自己,所以他必死,特判一下即可。这样我们就能O(n)O(n)O(n)计算出这个答案了。
然后我们来看怎么样死亡最少,也就是存活最多。注意到上面“一个人和他的目标不能都存活”这个性质,在图中就表现为,一条边的两个端点不可能都存活,于是我们要找的就是在这种状态下,最多能选出多少个人存活,这显然就是一个环套树上的最大独立集问题,只不过还要规定所有叶子节点都必须被选,用边界条件稍微修改的环套树DP就能O(n)O(n)O(n)解决。当然,还要特判环套树中的环中只有一个人的情况,这样的话这个人是必死的。把每个连通块的答案加起来,就是最多的存活人数了,最少的死亡人数也随之得出了。
于是经过上面的讨论,我们解决了这个问题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int n,a[1000010],in[1000010],q[1000010],h,t,ans1,ans2;
int f[1000010][2]={0},looplen,loop[1000010];
int loopf[2][2];
bool totflag,flag[1000010]={0},vis[1000010]={0};void find_loop(int i)
{looplen=1;totflag=flag[i];loop[1]=i;vis[i]=1;f[i][1]++;while(a[loop[looplen]]!=loop[1]){++looplen;loop[looplen]=a[loop[looplen-1]];totflag|=flag[loop[looplen]];vis[loop[looplen]]=1;f[loop[looplen]][1]++;}
}int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);in[a[i]]++;}h=1,t=0;ans2=n;for(int i=1;i<=n;i++)if (!in[i]){q[++t]=i;ans2--;}while(h<=t){int v=q[h++];in[a[v]]--;f[v][1]++;f[a[v]][0]+=max(f[v][0],f[v][1]);f[a[v]][1]+=f[v][0];if (!in[a[v]]) q[++t]=a[v];flag[a[v]]=1;}ans1=n;for(int i=1;i<=n;i++)if (in[i]&&!vis[i]){find_loop(i);if (!totflag&&looplen>1) ans2--;if (looplen==1) {ans1-=f[i][0];continue;}if (looplen==2){int x=i,y=a[i],mx=0;mx=max(mx,f[x][0]+f[y][0]);mx=max(mx,f[x][0]+f[y][1]);mx=max(mx,f[x][1]+f[y][0]);ans1-=mx;continue;}if (looplen==3){int x=i,y=a[i],z=a[a[i]],mx=0;mx=max(mx,f[x][0]+f[y][0]+f[z][0]);mx=max(mx,f[x][0]+f[y][0]+f[z][1]);mx=max(mx,f[x][0]+f[y][1]+f[z][0]);mx=max(mx,f[x][1]+f[y][0]+f[z][0]);ans1-=mx;continue;}int mx=0,now=1,past=0;loopf[past][0]=loopf[past][1]=0;for(int j=2;j<=looplen;j++){loopf[now][0]=max(loopf[past][0],loopf[past][1])+f[loop[j]][0];loopf[now][1]=loopf[past][0]+f[loop[j]][1];swap(now,past);}mx=max(mx,max(loopf[past][0],loopf[past][1])+f[loop[1]][0]);loopf[past][0]=loopf[past][1]=0;for(int j=3;j<=looplen-1;j++){loopf[now][0]=max(loopf[past][0],loopf[past][1])+f[loop[j]][0];loopf[now][1]=loopf[past][0]+f[loop[j]][1];swap(now,past);}mx=max(mx,max(loopf[past][0],loopf[past][1])+f[loop[1]][1]+f[loop[2]][0]+f[loop[looplen]][0]);ans1-=mx;}printf("%d %d",ans1,ans2);return 0;
}

【BZOJ1124】Mafia(POI2008)-环套树DP相关推荐

  1. BZOJ 1791 岛屿(环套树+单调队列DP)

    题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点 ...

  2. 基环树DP(bzoj 1040: [ZJOI2008]骑士)

    树:n个点n-1条边的连通图 基环树:n个点n条边的连通图,也就是一个环套着多棵树 基环树DP:找到环上任意相邻两点,断掉这两点之间的边,就形成了一棵树 之后对这两点分别进行一次树形DP即可 例题: ...

  3. 【暖*墟】#动态规划# 基环树DP的学习与练习

    因为弃置了 四边形不等式优化 ,所以DP的任务还剩下 基环树DP / 插头DP / 动态DP 当然,树形DP / 状压DP / 数位DP / 斜率优化DP 也还是要练习的...... 一 . 基环树的 ...

  4. 【NOIP校内模拟】T2 华莱士(环套树)

    其实就是要求最小的环套树森林 我们现在只考虑如何合并 设当前边的两个端点是x,y 若x,y在一个联通块里 那这个联通块要么是树 要么是环套树 假如是个环套树 加一条边后必定变成两个环 不符合要求 假如 ...

  5. [BZOJ2125]最短路(圆方树DP)

    题意:仙人掌图最短路. 算法:圆方树DP,$O(n\log n+Q\log n)$ 首先建出仙人掌圆方树(与点双圆方树的区别在于直接连割边,也就是存在圆圆边),然后考虑点u-v的最短路径,显然就是:在 ...

  6. 【BZOJ1040】【codevs1423】骑士,第一次的基环外向树DP

    传送门1 传送门2 思路: 好题 比较简单的DP思路 之前没写过基环树DP,第一次搞真心orz 我们发现这些元素是具有从属关系的 也就是说如果对"厌恶的骑士"两两相互连边,那么问题 ...

  7. 51nod1812树的双直径(换根树DP)

    传送门:http://www.51nod.com/Challenge/Problem.html#!#problemId=1812 题解:头一次写换根树DP. 求两条不相交的直径乘积最大,所以可以这样考 ...

  8. P1295 [TJOI2011]书架(线段树dp)

    P1295 [TJOI2011]书架(线段树dp) 我好菜 先考虑普通dp: d p i = m i n ( d p j + m a x ( h j + 1 , h j + 2 - , h i ) ) ...

  9. 洛谷P2495 [SDOI2011]消耗战(虚树dp)

    P2495 [SDOI2011]消耗战 题目链接 题解: 虚树\(dp\)入门题吧.虚树的核心思想其实就是每次只保留关键点,因为关键点的dfs序的相对大小顺序和原来的树中结点dfs序的相对大小顺序都是 ...

最新文章

  1. php下载提示保存,php利用header函数实现文件下载时直接提示保存_php技巧
  2. 清华数为大数据应用低代码开发工具DWF 2021成长回顾
  3. 使用KiWi Syslog Daemon构建日志服务器
  4. MyEcplise安装Freemarker插件(支持.ftl文件)
  5. C#.NET常见问题(FAQ)-命名空间namespace如何理解
  6. Python教程:threading中join与setDaemon的用法及区别讲解
  7. Debian 系统修改语言为英文
  8. 2020年,我来盘点下微服务架构技术栈
  9. leetcode 1442. 形成两个异或相等数组的三元组数目(位运算)
  10. java quartz 数据库_SpringBoot+Quartz+数据库存储
  11. 方程求解(洛谷P1689题题解,Java语言描述)
  12. 糖果传递(信息学奥赛一本通-T1432)
  13. vs 正则表达式转大写_liunx之通配符amp;正则表达式
  14. java 获取路径_java 获取当前类的路径
  15. 基于深度学习的咖啡叶病害识别和严重程度评估(源代码+数据集)
  16. python3哪个版本稳定-Python 3.9 发布稳定版本,八大特性学起来!
  17. WebSphere MQ通道的建立
  18. 孙正义:1万亿个物联网(IoT)器件正在加快人类进步
  19. mysql5.7 dmg安装
  20. Linux 编辑器 !强推!

热门文章

  1. kafka-4.进阶,ISR,OSR,AR,LW,HW,LEO,ACK原理理论
  2. 经典SQL学习笔记 (四)-子查询
  3. ROS 通信机制(已整理)
  4. UI设计教程之常见的APP小图标类型以及用途梳理
  5. X64汇编语言寄存器结构及其与X86架构编程区别
  6. Linux文件处理命令 file、mkdir、grep、dd、mv、diff
  7. 关于清理电脑系统垃圾的batch文件
  8. java8 List操作
  9. 组装计算机的游戏,如何组装游戏电脑
  10. 关于学计算机趣味段子,搞笑段子:路上一个女孩突然朝我走来问:你是不是学计算机的?...