Comet OJ

题意: 给定一颗无向无根树,求一大小为K的联通子图使得非子图最远点距离子图距离最小


树, 且联通

考虑平均砍去部分子树使得答案最优即可

所以从叶子入手一层一层往里砍, 砍几层答案是几

那么需要记录信息 p[y] 为以 y 为跟的子树最大深度为多少以 y 为根的子树最大深度为多少

所以要找个根,当树的中心为根时 p[y]最平均

所以两遍dfs求树中心即可

用树中心树上dp子树最大深度,

按深度遍历累加 当累加树达到时直接输出就完了。

如图 留3个时 ,树中心为1,红圈里的答案最优。

代码


/*
    Zeolim - An AC a day keeps the bug away
*/
 
//pragma GCC optimize(2)
//#pragma GCC ("-W1,--stack=128000000")
#include <bits/stdc++.h>
using namespace std;
#define mp(x, y) make_pair(x, y)
#define fr(x, y, z) for(int x = y; x < z; ++x)
typedef long long ll;
typedef double ld;
typedef std::pair<int, int> pii;
typedef std::vector <int> vi;
//typedef __int128 ill;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 1e9 + 7;
const int MAXN = 1e6 + 10;

vector <int> edge[MAXN];

int n, k;

int p[MAXN] = {0}, cnt[MAXN] = {0}, in[MAXN] = {0};

int lst = -1, lstlen = -1, rot = -1;

bitset <MAXN> used;

void dfs(int now, int step)
{
	used[now] = true;
	
	if(step > lstlen)
		lstlen = step, lst = now;
		
	for(auto x : edge[now])
	{
		if(!used[x])
			dfs(x, step + 1);
	}
	
	used[now] = false;
}

void gao1(int now, int step)
{
	used[now] = true;
	
	p[step] = now;
	
	if(step > lstlen)
		lstlen = step, lst = now, rot = p[step / 2];
		
	for(auto x : edge[now])
	{
		if(!used[x])
			gao1(x, step + 1);
	}
	
	used[now] = false;
}

void xiagao(int now, int fa)
{
	used[now] = true;
		
	for(auto x : edge[now])
	{
		if(!used[x])
			xiagao(x, now);
	}
	
	p[fa] = max(p[fa], p[now] + 1);
	
	used[now] = false;
}

void gao()
{
	used.reset();
	dfs(1, 0);
	
	used.reset();
	int nb = lst;
	
	lst = lstlen = -1;
	gao1(nb, 0);
	
	fill(p, p + n + 10, 1);
	used.reset();
	
	xiagao(rot, 0);
	
	for(int i = 1; i <= n; ++i)
		++cnt[p[i]];
		
	int sum = 0;
	
	for(int i = 1; i <= n; ++i)
	{
		sum += cnt[i];
		
		if(sum >= n - k)
		{
			cout << i << '
';
			return ;
		}
	}
}


int main()
{  
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	//freopen("d:out.txt","w",stdout);
	//freopen("in.txt","r",stdin);
	
	cin >> n >> k;
	
	for(int i = 1, x, y; i < n; ++i)
	{
		cin >> x >> y;
		
		edge[x].push_back(y);
		++in[x], ++in[y];
		edge[y].push_back(x);
	}
	
	gao();
	
    return 0;
}
/*
6 1
1 2
2 3
2 4
1 5
5 6
*/

后来发现求p也不用那么麻烦找中心,

可以用拓扑从叶子入手一层一层进行dp求p数组

这样也是均匀的

其他一样

/*
    Zeolim - An AC a day keeps the bug away
*/
 
//pragma GCC optimize(2)
//#pragma GCC ("-W1,--stack=128000000")
#include <bits/stdc++.h>
using namespace std;
#define mp(x, y) make_pair(x, y)
#define fr(x, y, z) for(int x = y; x < z; ++x)
typedef long long ll;
typedef double ld;
typedef std::pair<int, int> pii;
typedef std::vector <int> vi;
//typedef __int128 ill;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 1e9 + 7;
const int MAXN = 1e6 + 10;

vector <int> edge[MAXN];

int n, k;

int p[MAXN] = {0}, cnt[MAXN] = {0}, in[MAXN] = {0};


void gao()
{
	queue <int> Q;
	
	for(int i = 1; i <= n; ++i)
	{
		if(in[i] == 1)
			Q.push(i), p[i] = 1;
	}
	
	while(!Q.empty())
	{
		int now = Q.front();
		Q.pop();
		for(auto x : edge[now])
		{
			if(in[x] == 1) continue; //已经算过的叶子要直接跳过 不然影响p数组更新
			
			p[x] = max(p[x], p[now] + 1);
			
			if(--in[x] == 1) //无向图 入度为 1 即为叶子
				Q.push(x);
		}	
	}
	
	for(int i = 1; i <= n; ++i)
		++cnt[p[i]];
		
	int sum = 0;
	
	for(int i = 1; i <= n; ++i)
	{
		sum += cnt[i];
		
		if(sum >= n - k)
		{
			cout << i << '
';
			return ;
		}
	}
}

int main()
{  
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	//freopen("d:out.txt","w",stdout);
	//freopen("in.txt","r",stdin);
	
	cin >> n >> k;
	
	for(int i = 1, x, y; i < n; ++i)
	{
		cin >> x >> y;
		
		edge[x].push_back(y);
		++in[x], ++in[y];
		edge[y].push_back(x);
	}
	
	gao();
	
    return 0;
}
/*
6 1
1 2
2 3
2 4
1 5
5 6
*/
原文地址:https://www.cnblogs.com/zeolim/p/12270348.html