被教练勒令做题不能看题解后的第一道新题,自行 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. mess系统 开发技术,需求整理

    1.1.WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. Internet上供外界访问的Web资源分为: 静态web资源(如html 页 ...

  2. 1031. Hello World for U

    Given any string of N (>=5) characters, you are asked to form the characters into the shape of U. ...

  3. springcloud(九):熔断器Hystrix和Feign的应用案例

    因为 feign 中已经支持了 Hystrix ,所以在 Feign 中使用 Hystrix 时,不需要导包,也不需要在入口类上面增加额外的注解:  Feign 虽然支持了 Hystrix ,但是默认 ...

  4. java 访问对象私有变量

    Captcha captcha = getCaptcha(captchaId); // 通过反射获取验证码值 Class<?> classType = captcha.getClass() ...

  5. 封装的一些常见的JS DOM操作和数据处理的函数.

    //用 class 获取元素 function getElementsByClass(className,context) { context = context || document; if(do ...

  6. HDU 2242 连通分量缩点+树形dp

    题目大意是: 所有点在一个连通图上,希望去掉一条边得到两个连通图,且两个图上所有点的权值的差最小,如果没有割边,则输出impossible 这道题需要先利用tarjan算法将在同一连通分量中的点缩成一 ...

  7. [luoguP1111] 修复公路(并查集)

    传送门 呵呵的最小生成树 ——代码 #include <cstdio> #include <iostream> #include <algorithm> #defi ...

  8. - > 贪心基础入门讲解二——活动安排问题

    有若干个活动,第i个开始时间和结束时间是[Si,fi),只有一个教室,活动之间不能交叠,求最多安排多少个活动? 分析: 我们就是想提高教室地利用率,尽可能多地安排活动.考虑容易想到的几种贪心策略: ( ...

  9. [Vue @Component] Switch Between Vue Components with Dynamic Components

    A common scenario is to present different components based on the state of the application. Dynamic ...

  10. 利用scons构建project

    scons有非常多相对于make构建系统的优秀特性,可是因为发展时间比較短如今的应用范围还是不太多,可以找到的资料也不是非常多. scons如今一大问题就是初始上手还是有点难度的,对于有python的 ...