题解 将军令 (这里还有个没填的坑)

传送门

写特殊性质骗分的时候1打成2了挂了十分
还有以后写\(n<=16\)的部分分不要特判\(n<=16\),直接把能跑出来的范围都划进去

先说正解吧……正解是个贪心
众所周知,贪心题最大的特点就是考场上怎么看都觉得贪心不对,
然后看完题解恍然大悟noip里真的可以有贪心题
对于此题,「守卫应该放在最深的未被覆盖的点的k级祖先」的贪心思路应该很好想
但一般会因为考虑到下面这种情况而怀疑其正确性

RdQGRO.png

如果这里\(k=4\),那么点3没有4级祖先
这里比较迷的一个问题是如果点4下面又跟了一大串节点,会不会拐一下,放在点4更优呢?
并不会。
「守卫应该放在最深的未被覆盖的点的\(k\)级祖先」,
所以当我们开始处理点3的时候,树上已经没有比点3离根节点更远的点了
所以放在根节点一定能覆盖剩下的所有点,所以直接放根节点就可以了
所以可以贪心

然后我的解法是利用特殊性质状压骗分,就没什么好写的了
但是这题还有一种极其麻烦的解法:树形DP
鉴于贪心应该可以解决所有半径为\(k\)的覆盖问题,
而且这种树形DP的可扩展性不太强,我就先留坑待填了
可扩展性显然很强,考虑每个点的影响范围扩大到2

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 999999
#define N 100010
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, k, type;
int head[N], size, cnt[N], root;
bool vis[N], searched[N];
struct edge{int to, next;}; edge* e;
inline void add(int s, int t) {edge* k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;}

void dfs(int u, int fa, int dep) {
	searched[u]=1;
	for (int i=head[u],v; i; i=e[i].next) {
		v = e[i].to;
		if (v!=fa) {
			vis[v]=1;
			if (dep>1 && !searched[v]) dfs(v, u, dep-1);
		}
	}
}

namespace task1{
	// 0->被子节点点亮 2->被父节点点亮
	int dp[N][3];
	void dfs(int u, int fa) {
		if (u!=root&&cnt[u]==1) {dp[u][0]=INF; dp[u][1]=1; dp[u][2]=0; return ;}
		int dir=INF;
		for (int i=head[u],v; i; i=e[i].next) {
			v = e[i].to;
			if (v!=fa) {
				dfs(v, u);
				if (dp[v][0]>=dp[v][1]) dp[u][0]+=dp[v][1], dir=0;
				else dp[u][0]+=dp[v][0], dir=min(dir, dp[v][1]-dp[v][0]);
				dp[u][1]+=min(min(dp[v][0], dp[v][1]), dp[v][2]);
				dp[u][2]+=dp[v][0], dp[u][2]=min(dp[u][2], INF);
			}
		}
		dp[u][0]+=dir, dp[u][0]=min(dp[u][0], INF);
		++dp[u][1];
	}
	void solve() {
		//for (int i=1; i<=n; ++i) if (cnt[i]!=1) {dfs(i, 0); root=i; break;}
		root=1;
		dfs(1, 0);
		printf("%d\n", min(dp[root][0], dp[root][1]));
		//for (int i=1; i<=n; ++i) cout<<i<<": "<<dp[i][0]<<' '<<dp[i][1]<<' '<<dp[i][2]<<endl;
		exit(0);
	}
}

// 状压
namespace tasks{
	void solve() {
		int lim=(1<<n)-1, ans=INF;
		for (int s=1,cnt,s2; s<=lim; ++s) {
			memset(vis, 0, sizeof(bool)*(n+5));
			memset(searched, 0, sizeof(bool)*(n+5));
			s2=s; cnt=0;
			while (s2) {++cnt; s2&=s2-1;}
			if (cnt>=ans) continue;
			for (int i=0; i<n; ++i) {
				if (s&(1<<i)) {
					vis[i+1]=1;
					dfs(i+1, 0, k);
				}
			}
			for (int i=1; i<=n; ++i) if (!vis[i]) goto jump;
			ans=min(ans, cnt);
			jump: ;
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task2{
	int lst[20], tot=0;
	void solve() {
		for (int i=1; i<=n; ++i) if (cnt[i]>1) lst[++tot]=i;
		//cout<<"tot: "<<tot<<endl;
		//for (int i=1; i<=tot; ++i) cout<<lst[i]<<' '; cout<<endl;
		int lim=(1<<tot)-1, ans=INF;
		for (int s=1,cnt,s2; s<=lim; ++s) {
			memset(vis, 0, sizeof(bool)*(n+5));
			memset(searched, 0, sizeof(bool)*(n+5));
			s2=s; cnt=0;
			while (s2) {++cnt; s2&=s2-1;}
			if (cnt>=ans) continue;
			for (int i=0; i<tot; ++i) {
				if (s&(1<<i)) {
					vis[lst[i+1]]=1;
					dfs(lst[i+1], 0, k);
				}
			}
			for (int i=1; i<=n; ++i) if (!vis[i]) goto jump;
			ans=min(ans, cnt);
			jump: ;
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	int back[N], ans=0;
	struct ele{int rank, dep;}p[N];
	inline bool operator < (ele a, ele b) {return a.dep>b.dep;}
	void dfs2(int u, int fa) {
		for (int i=head[u],v; i; i=e[i].next) {
			v = e[i].to;
			if (v!=fa) {
				p[v].dep=p[u].dep+1;
				back[v]=u;
				dfs2(v, u);
			}
		}
	}
	void dfs3(int u, int fa, int dep) {
		for (int i=head[u],v; i; i=e[i].next) {
			v = e[i].to;
			if (v!=fa) {
				vis[v]=1;
				if (dep>1) dfs3(v, u, dep-1);
			}
		}
	}
	void solve() {
		memset(vis, 0, sizeof(vis));
		memset(searched, 0, sizeof(searched));
		for (int i=1; i<=n; ++i) p[i].rank=i;
		dfs2(1, 0);
		sort(p+1, p+n+1);
		int t;
		for (ele *it=p+1,*end=p+n+1; it!=end; ++it) {
			if (!vis[it->rank]) {
				t = it->rank;
				for (int j=1; j<=min(k, it->dep); ++j) t=back[t];
				vis[t]=1;
				dfs3(t, 0, k);
				++ans;
			}
		}
		//for (int i=1; i<=n; ++i) if (!vis[i]) cout<<i<<endl;
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read(); k=read(); type=read();
	e = new edge[n*2+10];
	for (int i=1,u,v; i<n; ++i) {u=read(); v=read(); add(u, v); add(v, u); ++cnt[u]; ++cnt[v];}
	
	if (!k) {printf("%d\n", n); return 0;}
	else if (k==1) task1::solve();
	else if (n<=20) tasks::solve();
	else if (type==1) task2::solve();
	else task::solve();

	return 0;
}
posted @ 2021-06-29 16:41  Administrator-09  阅读(16)  评论(0编辑  收藏  举报