虚树+Kingdom and its Cities

\(Firstly\),\(DP\)的好助手

适用前提

有些问题 \(n\)过大

导致\(m\)次询问 \(n*m\)太大

且每次DP的关键点是确定的

这时我们需要虚树

每一次树上\(DP\)

我们都构建虚树然后在树上\(DP\)

自然虚树上只包含 关键点 和关键点的\(LCA\)

直接枚举\(LCA\) 时间复杂度是不降反升的

\(Secondly\),实现

所以 如何保证复杂度 构建出虚树 就是虚树的核心了

大概方法是

将关键点按前序排序

然后最开始用栈维护一条链

若 此时的 \(u\)与栈顶元素的\(LCA\)不在链内

\(u\)与栈顶元素是\(LCA\)的两个不同子树的节点

(只要这句话懂了 就写得出虚树了)

明显 后期栈维护的不再只是一条链

这里用数组模拟栈

具体操作 看代码

inline void BuildTree(int m)
{
	top = 1,st[1] = 1,G[1].clear();
	for(reg i = 1;i <= m;i++)
	{
		if(poi[i] == 1) continue;
		int lc = lca(poi[i],st[top]);
		if(lc != st[top])
		{
			while(dfn[st[top - 1]] > dfn[lc]) add(st[top - 1],st[top]),top--;
			if(lc != st[top - 1]) G[lc].clear(),add(lc,st[top]),st[top] = lc;
			else add(lc,st[top--]);
		}
		G[poi[i]].clear(),st[++top] = poi[i];
	}
	for(reg i = 1;i < top;i++) add(st[i],st[i + 1]);
}

\(Finally\),例题

Kingdom and its Cities

#include <map>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= (x)&&(x) <= '9')
template<typename T>
inline T Read(T Type)
{
	T x = 0,f = 1;
	char a = getchar();
	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
	return x * f;
}
const int MAXN = 100010;
vector<int> G[MAXN];
int dp[MAXN],size[MAXN];
int dfn[MAXN],dep[MAXN],fa[MAXN][20],poi[MAXN],st[MAXN],top,cnt;
bool vis[MAXN];
inline void add(int u,int v) {G[u].push_back(v);G[v].push_back(u);}
inline void dfs(int x,int f)
{
	dfn[x] = ++cnt;
	for(reg i = 0;i < G[x].size();i++)
	{
		int v = G[x][i];
		if(v == f) continue;
		dep[v] = dep[x] + 1,fa[v][0] = x;
		dfs(v,x);
	}
}
inline void adjust(int &u,int val)
{
	for(reg i = 18;i >= 0;i--)
		if(dep[fa[u][i]] >= val)
			u = fa[u][i];
}
inline int lca(int u,int v)
{
	if(dep[u] > dep[v]) adjust(u,dep[v]);
	if(dep[v] > dep[u]) adjust(v,dep[u]);
	if(u == v) return v;
	for(reg i = 18;i >= 0;i--)
		if(fa[u][i] != fa[v][i])
			u = fa[u][i],v = fa[v][i];
	return fa[u][0];
}
bool cmp(int a,int b) {return dfn[a] < dfn[b];}
inline void BuildTree(int m)
{
	top = 1,st[1] = 1,G[1].clear();
	for(reg i = 1;i <= m;i++)
	{
		if(poi[i] == 1) continue;
		int lc = lca(poi[i],st[top]);
		if(lc != st[top])
		{
			while(dfn[st[top - 1]] > dfn[lc]) add(st[top - 1],st[top]),top--;
			if(lc != st[top - 1]) G[lc].clear(),add(lc,st[top]),st[top] = lc;
			else add(lc,st[top--]);
		}
		G[poi[i]].clear(),st[++top] = poi[i];
	}
	for(reg i = 1;i < top;i++) add(st[i],st[i + 1]);
}
inline void dfs2(int x,int f)
{
	dp[x] = size[x] = 0;
	for(reg i = 0;i < G[x].size();i++)
	{
		int v = G[x][i];
		if(v == f) continue;
		dfs2(v,x);
		dp[x] += dp[v],size[x] += size[v]; 
	}
	if(vis[x]) dp[x] += size[x],size[x] = 1;
	else if(size[x] > 1) dp[x]++,size[x] = 0;
}
int main()
{
	int n = Read(1);
	for(reg i = 1;i < n;i++)
	{
		int u = Read(1),v = Read(1);
		add(u,v);
	}
	dep[1] = 1,dfs(1,0);
	for(reg i = 1;i <= 18;i++)
		for(reg j = 1;j <= n;j++)
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
	int T = Read(1);
	while(T--)
	{
		memset(vis,0,sizeof(vis));
		int m = Read(1);
		for(reg i = 1;i <= m;i++)
		{
			poi[i] = Read(1);
			vis[poi[i]] = 1;
		}
		bool able = 0;
		for(reg i = 1;i <= m;i++)
			if(vis[fa[poi[i]][0]]&&poi[i] != 1)
			{
				printf("-1\n");
				able = 1;
				break;
			}
		if(able) continue;
		sort(poi + 1,poi + 1 + m,cmp);
		BuildTree(m);
		dfs2(1,0);
		printf("%d\n",dp[1]); 
	}
    return 0;
}
posted @ 2019-10-20 16:25  resftlmuttmotw  阅读(127)  评论(0编辑  收藏  举报