题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=1124

https://www.luogu.org/problemnew/show/P3472

分析

首先, 每个神枪手都只有一个目标.

若是把每个神枪手当成一个点来建图, 那么这个图每个点的出度都是1(基环内向树)

既然\(N \leq 10^6\), 这道题基本上是个贪心.

分别考虑最少存活人数和最大存活人数的求解. (死亡人数 = N - 存活人数)

1. 最少存活人数(minlive)

若是一个点入度为0, 那么这个点必定存活.

对于基环内向森林中的每一个基环内向树, 判断其中是否有入度为0的点.

若有, 那么这个基环内向树中除了入度为0的点其他点都可以被杀(环上的点先开枪使环上只留1个点, 再按照拓扑序逆序开枪即可)

此时minlive += cnt[ ind==0 ]

若无, 那么其中必定1个点可以存活, minlive++(环长为1的需要特判).

2. 最多存活人数(maxlive)

这个问题相对复杂.

先考虑内向树上maxlive的求解.

内向树上叶子节点必定存活. 而且, 从下到上, 每一层的节点个数都\(\ge\) 其上一层节点个数.所以取从下到上的1, 3, 5, 7....层是最优方案.

如: 这棵内向树有3层.

所以, 将入度为0的点(最下层点集)取出, 用类似于拓扑排序分层的方法, 隔一层向队列中加点即可(详见代码).

具体来说, 如果a杀死了b, 那么b指向的人c就少了一个可以杀死他的人. 所以把c的入度-1. 当c入度为0时, c必定存活, 那么可以把c加入队列, 作为a这一层之后的"活人"层(这一点与拓扑排序相同)

再考虑基环内向树上maxlive的求解.

用以上算法求解过后, 图中必定剩下若干个环.

这些环对maxlive的贡献是 环长/2.

code

#include<bits/stdc++.h>
using namespace std; typedef long long ll;
#define rep(i, j, k) for(register int i=(j);i<=(k);++i)
#define per(i, j, k) for(register int i=(j);i>=(k);--i) int read(){
int ret = 0, f = 1; char c=getchar();
while(isdigit(c) == false) {
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c)) ret = ret*10+c-'0', c=getchar();
return ret*f;
}
const int maxn= 1e6+5;
int n;
int to[maxn], ind[maxn];
int L, R, q[maxn];
int maxlive, minlive; bool dead[maxn], vis[maxn];
void getdead(){
minlive = 0;
L = R = 1; //[L, R)
rep(i, 1, n){
if(ind[i] == 0){
minlive++;
q[R++] = i; //将入度为0的点加入队列
for(int cur = i; !vis[cur]; vis[cur] = 1, cur = to[cur]);
}
}
rep(i, 1, n) if(!vis[i]){
int looplen = 0;
for(int cur = i; !vis[cur]; vis[cur] = 1, cur = to[cur])looplen++;
if(looplen != 1) {
minlive += 1;
}
} //calculate minlive memset(vis, 0, sizeof vis);
while(R - L > 0){
int cur = q[L++], u = to[cur];
if(vis[cur]) continue;
maxlive++, vis[cur] = 1;//cur 存活
if(vis[u] == 0){ //给to[u]除去一个威胁
vis[u] = 1, ind[to[u]]--;
if(ind[to[u]] == 0) q[R++] = to[u];
}
assert(u <= n);
}
// printf("bfs : %d %d\n" ,maxlive, minlive);
rep(i, 1, n) if(!vis[i]) {
int looplen = 0, cur;
for(cur = i; !vis[cur]; vis[cur] = 1, cur = to[cur])looplen++;
if(cur == i) maxlive += (looplen)/2;
else maxlive += (looplen+1)/2;
} //calculate maxlive
// printf("clearloop : %d %d\n" ,maxlive, minlive); }
signed main(){
// freopen("5.in", "r", stdin);
n =read();
rep(i, 1, n) {
to[i] = read();
ind[to[i]]++;
}
getdead();
printf("%d %d\n", n - maxlive, n - minlive);
return 0;
}

bzoj1124_枪战_基环树的更多相关文章

  1. [bzoj3037/2068]创世纪[Poi2004]SZP_树形dp_并查集_基环树

    创世纪 SZP bzoj-3037/2068 Poi-2004 题目大意:给你n个物品,每个物品可以且仅可以控制一个物品.问:选取一些物品,使得对于任意的一个被选取的物品来讲,都存在一个没有被选取的物 ...

  2. 【LOJ523】[LibreOJ β Round #3]绯色 IOI(悬念)(霍尔定理_基环树)

    题目 LOJ523 官方题解 分析 由于某些原因,以下用「左侧点」和「右侧点」分别代替题目中的「妹子」和「男生」. 根据题意,显然能得出一个左侧点只能向一个或两个右侧点连边.这似乎启发我们把左侧点不看 ...

  3. [bzoj1040][ZJOI2008]骑士_树形dp_基环树_并查集

    骑士 bzoj-1040 ZJOI-2008 题目大意:n个骑士,每个骑士有权值val和一个讨厌的骑士.如果一个骑士讨厌另一个骑士那么他们将不会一起出战.问出战的骑士最大atk是多少. 注释:$1\l ...

  4. Hdu第八场 树形dp+基环树

    Card Game 每个牌背面的数字朝正面的数字连一条有向边 则题目变为问你最少翻转多少次 能使得每个数字的入度不超过1 首先判断图中每个连通块是不是树或者基环树 因为只有树或者基环树能使得每个点的入 ...

  5. 【BZOJ1791】【IOI2008】【基环树】island(status第一速度)

      1791: [Ioi2008]Island 岛屿  Time Limit: 20 Sec  Memory Limit: 162 MB Submit: 908  Solved: 159 [Su ...

  6. BZOJ_4636_蒟蒻的数列_线段树+动态开点

    BZOJ_4636_蒟蒻的数列_线段树+动态开点 Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将 ...

  7. BZOJ_4627_[BeiJing2016]回转寿司_离散化+树状数组

    BZOJ_4627_[BeiJing2016]回转寿司_离散化+树状数组 Description 酷爱日料的小Z经常光顾学校东门外的回转寿司店.在这里,一盘盘寿司通过传送带依次呈现在小Z眼前.不同的寿 ...

  8. BZOJ_3252_攻略_线段树+dfs序

    BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...

  9. BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针

    BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间, ...

随机推荐

  1. docker学习------记录centos7.5下docker安装更换国内源的处理过程

    一.centos7.5下更换阿里源 1.装好centos7.5镜像,将yum源更换为阿里源 第一步:刚出的centos7.5是解析不到阿里的东西的,所以找了台centos7.4,下载一些包 (1) 下 ...

  2. #0 scrapy爬虫学习中遇到的坑记录

    python 基础学习中对于scrapy的使用遇到了一些问题. 首先进行的是对Amazon.cn的检索结果页进行爬取,很顺利,无碍. 下一个目标是对baidu的搜索结果进行爬取 1,反爬虫 1.1 我 ...

  3. UOJ #310「UNR #2」黎明前的巧克力

    神仙题啊... UOJ #310 题意 将原集合划分成$ A,B,C$三部分,要求满足$ A,B$不全为空且$ A$的异或和等于$ B$的异或和 求方案数 集合大小 $n\leq 10^6$ 值域$v ...

  4. Python Cookbook 数据结构和算法

    1.查找最大或最小的N个元素 import heapq nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2] print(heapq.nlargest(3, n ...

  5. noip2018游(AFO)记

    Day 0 到学车了,已经差不多四点了,领完一小袋比赛要用的就匆匆回了宾馆. 话说之前看地图的时候我们的宾馆最远,而且名字听起来并没有怎么高大上, 一看隔壁度豪大酒店就感觉应该比我们的酒店好.然鹅到了 ...

  6. linux下动态链接库(.so)的显式调用和隐式调用

    进入主题前,先看看两点预备知识. 一.显式调用和隐式调用的区别 我们知道,动态库相比静态库的区别是:静态库是编译时就加载到可执行文件中的,而动态库是在程序运行时完成加载的,所以使用动态库的程序的体积要 ...

  7. 024_mac配置屏保命令

    注意吃饭等离开工位的时候养成随时开启屏保的功能,养成信息保护的好习惯,mac如何配置屏幕保护呢? 一. 通过mac"设置"里的"Desktop & Screen ...

  8. W3CSchool实战闯关笔记(JavaScript)

    //handsome /** *ugly **/ 第一关注释 // 举例 var myName; // Define myName below this line 第二关声明变量 // Setup v ...

  9. java sigar获取本地信息以及org.hyperic.sigar.SigarException: The device is not ready报错解决

    window下,使用java sigar 获取磁盘使用率,cpu使用率以及内存使用情况等信息时. 一:首先需要下载jar包和相关文件 sigar-1.6.4.zip 如果想了解更多可以去 sigar官 ...

  10. 末学者笔记--shell编程上 1 玄

    Shell 编程 [内容简列]: 1.shell简介 2. shell分类 3. 查看shell 4. 第一个shell脚本 5. shell编程常用命令 5.1 grep 5.2 cut 5.3 s ...