BZOJ 1124: [POI2008]枪战Maf(构造 + 贪心)

题意

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

因此,对于不同的开枪顺序,最后死的人也不同。

问最后死的人数最小值以及最大值。 (1n106)

题解

不难发现是一道思博构造题。

首先考虑下最大值,我们只需要把这张图分三种情况讨论:

  1. 单个自环,贡献为 1
  2. 一个大于 1 的环,贡献为 len1
  3. 一个基环树,贡献为 sizenumleaf

对于最小值的话,我们可以不断模拟。

具体来说就是一开始把入度为 0 的人加入,这些人是活着的,然后他们瞄准的人就是必死的。

每次考虑连续三个点就行了,他们的顺序就是 活 活 的。(其实第三个人不一定活的)

然后我们对于第三个点的入度就会 1 ,如果为 0 我们加进队列继续操作。(这就意味着,它在其中任何一次死了。我们都不会加进去)

然后这样不断操作,最后只会留下环,不难发现环上至少死 len2 个人,这样就可以做完了qwq

总结

构造题认真想想还是想的出来的,但是需要大胆猜想小心求证才行qwq

代码

这道题实现起来其实有一些巧妙之处,建议读者仔细阅读。(参考了一下 ACist大佬的博客 )

我这个代码加上流输入,就可以取得 BZOJ 速度 rank1 的好成绩qwq

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << x << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} namespace pb_ds { namespace io { const int MaxBuff = 1 << 16; const int Output = 1 << 22; char B[MaxBuff], *S = B, *T = B; #define getc() ((S == T) && (T = (S = B) + fread(B, 1, MaxBuff, stdin), S == T) ? 0 : *S++) char Out[Output], *iter = Out; inline void flush() { fwrite(Out, 1, iter - Out, stdout); iter = Out; } } template<class Type> inline Type read() { using namespace io; register char ch; register Type ans = 0; register bool neg = 0; while(ch = getc(), (ch < '0' || ch > '9') && ch != '-') ; ch == '-' ? neg = 1 : ans = ch - '0'; while(ch = getc(), '0' <= ch && ch <= '9') ans = ans * 10 + ch - '0'; return neg ? -ans : ans; } } using namespace pb_ds; void File() { #ifdef zjp_shadow freopen ("1124.in", "r", stdin); freopen ("1124.out", "w", stdout); #endif } const int N = 1e6 + 1e3; int n, aim[N], deg[N], maxlive, minlive; bitset<N> dead, arrive; int main () { File(); n = read<int>(); For (i, 1, n) ++ deg[aim[i] = read<int>()]; queue<int> Q; For (i, 1, n) if (!deg[i]) Q.push(i), ++ minlive; while (!Q.empty()) { int u = aim[Q.front()], v = aim[u]; Q.pop(); ++ maxlive; if (dead[u]) continue ; dead[u] = true; arrive[v] = true; if (!(-- deg[v])) Q.push(v); } For (i, 1, n) if (deg[i] && !dead[i]) { int len = 0; bool flag = false; for (register int u = i; !dead[u]; u = aim[u]) dead[u] = true, ++ len, flag |= arrive[u]; if (!flag && len > 1) ++ minlive; maxlive += len / 2; } printf ("%d %d\n", n - maxlive, n - minlive); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9477479.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(252)  评论(2编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示