[bzoj1124][POI2008]枪战Maf——贪心 大佬们的博客 Some Links

版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/82889796

题目大意:

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

思路:

首先考虑最小,叶子结点杀的人必定会死,那么它就不要再杀人了,直接将他干掉。不断地从叶子结点入手,最后只会剩下一些环,环上的答案即 s i z e 2 \lceil \frac{size}{2}\rceil
考虑最大,叶子结点必定会活下来,一个环套树除了叶子结点都会死光,一个单纯的环(除了自环)最多死 s i z e 1 size-1 个人。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj1124.in","r",stdin);
	freopen("bzoj1124.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=1e6+10;
int n,to[maxn];

namespace GetMin{
	bool die[maxn],vis[maxn];
	int deg[maxn],sum,fa[maxn],sz[maxn];
	queue<int>qu;
	int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
	void solve(){
		REP(i,1,n)++deg[to[i]];
		REP(i,1,n)if(!deg[i])qu.push(i);
		while(!qu.empty()){
			int u=qu.front();
			qu.pop(); vis[u]=1;
			if(!die[to[u]]){
				vis[to[u]]=1; die[to[u]]=1;
				if(!die[to[to[u]]]){
					--deg[to[to[u]]];
					if(!deg[to[to[u]]])
						qu.push(to[to[u]]);
				}
			}
		}
		REP(i,1,n)sum+=die[i],fa[i]=i,sz[i]=1;
		REP(i,1,n)if(!vis[i]){
			int fx=find(i),fy=find(to[i]);
			if(fx!=fy){
				fa[fx]=fy;
				sz[fy]+=sz[fx];
			}
		}
		REP(i,1,n)if(!vis[find(i)]){
			vis[find(i)]=1;
			sum+=(sz[find(i)]-1)/2+1;
		}
	}
}

namespace GetMax{
	//所有的叶子结点都可以活下来。
	//如果只有单纯的环(除了自环),环上可以存活一个人。
	int deg[maxn],sum,fa[maxn],sz[maxn];
	bool tag[maxn],vis[maxn];
	int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
	void solve(){
		sum=n;
		REP(i,1,n)++deg[to[i]],fa[i]=i,sz[i]=1;
		REP(i,1,n)if(!deg[i])--sum;
		REP(i,1,n){
			int fx=find(i),fy=find(to[i]);
			if(fx!=fy){
				fa[fx]=fy;
				sz[fy]+=sz[fx];
			}
		}
		REP(i,1,n)if(deg[i]>=2)tag[find(i)]=1;
		REP(i,1,n)if(!vis[find(i)] && !tag[find(i)] && to[find(i)]!=find(i))
			vis[find(i)]=1,--sum;
	}
}

int main(){
	File();
	read(n);
	REP(i,1,n)read(to[i]);
	GetMin::solve();
	GetMax::solve();
	printf("%d %d\n",GetMin::sum,GetMax::sum);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/82889796