bzoj3572 世界树 虚树&树形dp

该博客探讨了一道典型的虚树问题——bzoj3572。通过构建关键点的虚树,两遍遍历计算每个点到最近关键点的距离和编号。博主详细解释了如何处理虚树中父子节点对答案的影响,并提出寻找分界点的方法。通过子树大小统计,可以计算路径上的点数,最终AC代码由lych在2016.3.7提供。
摘要由CSDN通过智能技术生成

       很明显的虚树题。

       把关键点的虚树构建出来,然后可以两遍遍历得到离点i最近的关键点的距离和编号。那么现在考虑虚树中的一对点(x,y),x为y的某个儿子,考虑其对答案的影响。

       由于是虚树,那么显然所有y->x的路径上的点,这个点延伸出去的点中(不包含由y->x的路径)不会有关键点存在,那么离这些点最近的虚树中的点,要么是x,要么是y,而且一定是先到达y->x的路径上的某一点,然后到达x或y,最后到达关键点。那么y->x的路径上有有一个分界点z,使得y->z的路径上延伸出去的点都由y到达关键点,z->x都由x到达关键点。而x到关键点的路径一定是往下走,y则一定是先往上走,因此求出关于x个y的两个关键点的中点,就是z了。

       然后用子树大小统计路径及其延伸出去的点的个数即可,最后要加上那些没有被统计到的点。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define inf 1000000000
using namespace std;

int n,m,tot,dfsclk,bin[25],fst[N],pnt[N<<1],nxt[N<<1],fa[N][19],d[N],pos[N];
int a[N],p[N],id[N],val[N],anc[N],len[N],ans[N],sz[N],q[N]; struct node{ int x,y; }g[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	pos[x]=++dfsclk; sz[x]=1; int p,i;
	for (i=1; bin[i]<=x; i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x][0]){
			fa[y][0]=x; d[y]=d[x]+1;
			dfs(y); sz[x]+=sz[y];
		}
	}
}
int lca(int x,int y){
	if (d[x]<d[y]) swap(x,y); int tmp=d[x]-d[y],i;
	for (i=0; bin[i]<=tmp; i++)
		if (tmp&bin[i]) x=fa[x][i];
	for (i=18; i>=0; i--)
		if (fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; }
	return (x==y)?x:fa[x][0];
}
int find(int x,int dep){
	int i; for (i=18; i>=0; i--) if (d[fa[x][i]]>=dep) x=fa[x][i];
	return x;
}
bool cmp(int x,int y){ return pos[x]<pos[y]; }
bool lss(node u,node v){
	return u.x<v.x || u.x==v.x && u.y<v.y;
}
void solve(){
	m=read(); int i,cnt=m,tp=0; node t;
	for (i=1; i<=m; i++){
		a[i]=id[i]=p[i]=read(); g[a[i]].y=a[i];
		g[a[i]].x=ans[a[i]]=0;
	}
	sort(a+1,a+m+1,cmp);
	for (i=1; i<=m; i++)
		if (!tp){ q[++tp]=a[i]; anc[a[i]]=0; } else{
			int tmp=lca(a[i],q[tp]);
			for (; d[q[tp]]>d[tmp]; tp--)
				if (d[q[tp-1]]<=d[tmp]) anc[q[tp]]=tmp;
			if (q[tp]!=tmp){
				p[++cnt]=tmp; anc[tmp]=q[tp];
				q[++tp]=tmp; g[tmp].x=inf; g[tmp].y=0;
			}
			anc[a[i]]=tmp; q[++tp]=a[i];
		}
	sort(p+1,p+cnt+1,cmp);
	for (i=1; i<=cnt; i++){
		int x=p[i]; val[x]=sz[x];
		if (i>1) len[x]=d[x]-d[anc[x]];
	}
	for (i=cnt; i>1; i--){
		int x=p[i]; t=g[x]; t.x+=len[x];
		if (lss(t,g[anc[x]])) g[anc[x]]=t;
	} 
	for (i=2; i<=cnt; i++){
		int x=p[i]; t=g[anc[x]]; t.x+=len[x];
		if (lss(t,g[x])) g[x]=t;
	}
	for (i=1; i<=cnt; i++){
		int x=p[i],y=anc[x];
		if (i==1) ans[g[x].y]+=n-sz[x]; else{
			int tmp=find(x,d[y]+1),sum=sz[tmp]-sz[x];
			val[y]-=sz[tmp];
			if (g[x].y==g[y].y) ans[g[x].y]+=sum; else{
				int z=d[x]-((g[y].x+len[x]-g[x].x)>>1);
				if (!((g[y].x+g[x].x+len[x])&1) && g[x].y>g[y].y) z++;
				z=sz[find(x,z)]-sz[x];
				ans[g[x].y]+=z; ans[g[y].y]+=sum-z;
			}
		}
	}
	for (i=1; i<=cnt; i++) ans[g[p[i]].y]+=val[p[i]];
	for (i=1; i<=m; i++) printf("%d ",ans[id[i]]); puts("");
}
int main(){
	n=read(); int i;
	bin[0]=1; for (i=1; i<=19; i++) bin[i]=bin[i-1]<<1;
	for (i=1; i<n; i++){
		int x=read(),y=read();
		add(x,y); add(y,x);
	}
	d[1]=1; dfs(1);
	int cas=read(); while (cas--) solve();
	return 0;
}


by lych

2016.3.7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值