被教练勒令做题不能看题解后的第一道新题,自行 yy 了好久终于 AC 了(菜啊)……写博客纪念。

题目:

BZOJ1124

分析:

考虑每个人向他要打的人连边。根据题意,所有点都有且只有一条出边。那么这个图一定是由若干个环、和若干个基环内向树组成(如果想不明白这句话,请时刻牢记每个点只有一条出边)。下面分别考虑两问。

存活人数最小:

核心思想:尽量打死已经开枪的人,让每个人的枪充分发挥作用。

对于环,如果是只有一个点的自环(即这货想不开要自杀),则这个人必死无疑。否则,每个人在打死他的后继后被他的前驱(如果还活着)打死,最后只剩下 \(1\) 个人。即按照边的相反方向依次开枪。

对于环套树,首先可以按照环的方法处理基环,让最后剩下的那个人是某棵树的根,再让他被树上的儿子打死。对于树上的点,一定是每个人打死他的父亲后被他的儿子打死,最后剩下的人数是所有树的叶子(定义为入度为 \(0\) 的点)数量之和。

(我的实现方式比较麻烦:先找出所有环的数量,再找出有多少个环上“插”着树,存活的人数就是 “环的数量 - 插着树的环的数量 - 自杀的数量 + 叶子的数量” )

存活人数最大:

核心思想:尽量打死还没有开枪的人,防止他去毒害别人。

对于环,每个人(如果还活着)顺着边的方向依次开枪,这样活下来的是初始点后继的后继、后继的后继的后继的后继……(环长是奇数时初始点会死)这样能使环上存活人数最大(环长的一半向下取整)。

对于环套树,由于叶子一定存活,所以他们的父亲一定会死,所以不让叶子的父亲开枪是比较优的。由此推广,按照从下往上的顺序开枪能使存活人数最大。(环上的树根也是能打就打。由于这样只是避免了树根去打别的环上的点,所以答案不会更劣。)

树上决策完后,环可能被拆成了若干条链(也可能还是完整的环),然后按照类似于环的决策(隔一个活一个)即可。注意链一定要保证链首存活,否则如果链长是奇数会让答案小 \(1\) 。

代码:

细节比较多(当然还有一个原因就是我写得丑,各位大佬可以自己写)。

注意求最大存活人数一定要按照先树再环 / 链的顺序处理,以及要保证对于所有链都必须从链首开始处理。我的做法是处理完树后删除所有基环上的死人为它的后继提供的度数,然后从所有此时度数为 \(0\) 且初始在环上(我的方式是判断处理完树后的度数是否为 \(0\) )的点开始处理(即链),最后从处理所有存活且后继也存活的点开始处理(即环)。


#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <queue>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
const int N = 1e6 + 10;
int n, to[N], deg[N], in[N], belong[N], cirnum;
bool dead[N], insta[N], vis[N], have_tree[N];
bool dfs(const int u)
{
if (vis[u])
{
have_tree[belong[u]] = true;
return false;
}
if (insta[u])
{
belong[u] = u;
cirnum++;
return true;
}
insta[u] = true;
if (dfs(to[u]))
{
vis[u] = true;
insta[u] = false;
belong[u] = belong[to[u]];
return u != belong[u];
}
else
{
insta[u] = false;
vis[u] = true;
if (belong[to[u]])
have_tree[belong[to[u]]] = true;
return false;
}
}
int solve_max()
{
memset(vis, 0, sizeof(bool[n + 1]));
for (int i = 1; i <= n; i++)
if (!vis[i])
dfs(i);
int ans = cirnum;
for (int i = 1; i <= n; i++)
{
if (!deg[i])
++ans;
if (to[i] == i || (belong[i] == i && have_tree[i]))
--ans;
}
return n - ans;
}
void solve_cir(const int u)
{
vis[u] = true;
if (!dead[u])
dead[to[u]] = true;
if (!vis[to[u]])
solve_cir(to[u]);
}
int solve_min()
{
static queue<int> q;
memset(vis, 0, sizeof(bool[n + 1]));
for (int i = 1; i <= n; i++)
if (!in[i])
q.push(i);
while (!q.empty())
{
int u = q.front();
q.pop();
--in[to[u]];
if (!dead[u] && !dead[to[u]])
dead[to[u]] = true;
if (!in[to[u]])
q.push(to[u]);
}
static int tmp[N];
memcpy(tmp, in, sizeof(int[n + 1]));
int ans = 0;
for (int i = 1; i <= n; i++)
if (dead[i] && tmp[i] && tmp[to[i]])
--in[to[i]];
for (int i = 1; i <= n; i++)
if (tmp[i] && !in[i])
solve_cir(i);
for (int i = 1; i <= n; i++)
if (!dead[i] && !dead[to[i]])
solve_cir(i);
for (int i = 1; i <= n; i++)
ans += dead[i];
return ans;
}
int work()
{
read(n);
for (int i = 1; i <= n; i++)
read(to[i]), ++deg[to[i]], ++in[to[i]];
write(solve_min()), putchar(' '), write(solve_max());
return 0;
}
}
int main()
{
return zyt::work();
}

【BZOJ1124】[POI2008]枪战Maf(基环树_构造)的更多相关文章

  1. BZOJ1124 [POI2008]枪战Maf[贪心(证明未完成)+拓扑排序]

    吐槽:扣了几个小时,大致思路是有了,但是贪心的证明就是不会, 死磕了很长时间,不想想了,结果码代码又不会码.. 深深体会到自己码力很差,写很多行还没写对,最后别人代码全一二十行,要哭了 以下可能是个人 ...

  2. BZOJ1124 POI2008枪战Maf(环套树+贪心)

    每个点出度都为1,可以发现这张图其实是个环套树森林,树中儿子指向父亲,环上边同向. 首先自环肯定是没救的,先抬出去. 要使死亡人数最多的话,显然若一个点入度为0其不会死亡,而一个孤立的环至少会留下一个 ...

  3. bzoj1124[POI2008]枪战maf

    这代码快写死我了.....死人最多随便推推结论.死人最少,每个环可以单独考虑,每个环上挂着的每棵树也可以分别考虑.tarjan找出所有环,对环上每个点,求出选它和不选它时以它为根的树的最大独立集(就是 ...

  4. 【BZOJ 1124】[POI2008] 枪战Maf Tarjan+树dp

    #define int long long using namespace std; signed main(){ 这个题一看就是图论题,然后我们观察他的性质,因为一个图论题如果没有什么性质,就是真· ...

  5. 【BZOJ1124】[POI2008]枪战Maf 贪心+思路题

    [BZOJ1124][POI2008]枪战Maf Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开 ...

  6. BZOJ 1124: [POI2008]枪战Maf

    1124: [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 617  Solved: 236[Submit][Status ...

  7. [POI2008]枪战Maf

    [POI2008]枪战Maf 题目 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的 ...

  8. [POI2008]枪战Maf题解

    问题 C: [POI2008]枪战Maf 时间限制: 1 Sec  内存限制: 256 MB 题目描述 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺 ...

  9. bzoj 1124 [POI2008]枪战Maf 贪心

    [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 741  Solved: 295[Submit][Status][Disc ...

随机推荐

  1. centos7安装:license information(license not accepted)

    安装centos7的时候明明已经选择了默认的许可证信息,不知道哪里出错了,安装到最后,就会显示license information(license not accepted)的信息.解决方法如下: ...

  2. HDU 4598 Difference

    Difference Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total ...

  3. Codeforces Round #254 (Div. 1) C DZY Loves Colors

    http://codeforces.com/contest/444/problem/C 题意:给出一个数组,初始时每个值从1--n分别是1--n.  然后两种操作. 1:操作 a.b内的数字是a,b内 ...

  4. noip模拟赛 剪纸

    题目描述 小芳有一张n*m的长方形纸片.每次小芳将会从这个纸片里面剪去一个最大的正方形纸片,直到全部剪完(剩下一个正方形)为止. 小芳总共能得到多少片正方形纸片? 输入输出格式 输入格式: 一行两个整 ...

  5. SGU -1500 - Pass Licenses

    先上题目: 1500. Pass Licenses Time limit: 2.5 secondMemory limit: 64 MB A New Russian Kolyan believes th ...

  6. Just a Hook 线段树 区间更新

    Just a Hook In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of t ...

  7. Maven安装好后包下载的测试命令和配置变量的查看命令:mvn help:system

    mvn help:system 该命令会打印出所有的Java系统属性和环境变量,这些信息对我们日常的编程工作很有帮助.运行这条命令的目的是为了让Maven执行一个真正的任务.我们可以从命令行输出看到M ...

  8. vue2的简单时间选择组件

    github:  https://github.com/longfei59418888/vui   (记得给一个 start,以后有一起讨论,各种好组件) demo : http://60.205.2 ...

  9. Cocos2d-x 3.x 图形学渲染系列十一

    笔者介绍:姜雪伟.IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

  10. Servlet-SrpingMVC 生成验证码

    在SpringMVC中配置生成验证码: import org.springframework.stereotype.Controller; import org.springframework.web ...