[FROM WOJ]#3604 [POI2014]HOT_Hotels(数据有加强)

#3604 [POI2014]HOT_Hotels(数据有加强)

题面
有一个树形结构的宾馆,n (1≤n≤100 000)个房间,n-1条无向边,每条边的长度相同,任意两个房间可以相互到达。吉丽要给他的三个妹子各开(一个)房(间)。三个妹子住的房间要互不相同(否则要打起来了),为了让吉丽满意,你需要让三个房间两两距离相同。 有多少种方案能让吉丽满意?

输入
第一行一个数n。
接下来n-1行,每行两个数x,y,表示x和y之间有一条边相连。

输出
让吉丽满意的方案数。

样例输入
7
1 2
5 7
2 5
2 3
5 6
4 5

样例输出
5

SOL
设:
f(i,j)=∑x∈subtree(i) [dis(i,x)=j]
g(i,j)=∑lca(x,y)∈subtree(i) [dis(x,lca)=dis(y,lca)=dis(i,lca)+j]
每次将一个儿子y转移到x:
ans+=∑f(y,i)∗g(x,i+1)+g(y,i)∗f(x,i−1)
f(x,i+1)+=f(y,i)
g(x,i−1)+=g(y,i)
g(x,i+1)+=f(x,i+1)∗f(y,i) (lca为x)

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
inline int rd(){
	int data=0;static char ch=0;ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data;
}
inline void write(int x){if(x>9)write(x/10);putchar(x%10+'0');}
const int N=1e6+5;
bool vis[N];
int f[N*10],g[N*10],n,ans,li,cnt,first[N],tcnt,pos[N],dfn[N],top[N],len[N],lson[N],fa[N],dep[N];
struct node{int v,nxt;}e[N<<1];
inline void add(int u,int v){e[++cnt]=(node){v,first[u]};first[u]=cnt;}
inline void dfs1(int u){
	len[u]=1,vis[u]=true;
	for(int re i=first[u];i;i=e[i].nxt){
		int re v=e[i].v;
		if(vis[v])continue;
		fa[v]=u,dep[v]=dep[u]+1,dfs1(v);
		len[u]=max(len[u],len[v]+1);
		if(len[lson[u]]<len[v]+1)lson[u]=v;
	}
}
inline void dfs2(int u){
	dfn[u]=++tcnt;
	if(lson[u])top[lson[u]]=top[u],dfs2(lson[u]);
	for(int re i=first[u];i;i=e[i].nxt){
		int re v=e[i].v;
		if(v==lson[u]||v==fa[u])continue;
		top[v]=v,pos[v]=li+len[v],li+=len[v]<<1,dfs2(v);
	}
}
inline int get_f(int u,int i){return dfn[u]+i;}
inline int get_g(int u,int i){return pos[top[u]]-dep[u]+dep[top[u]]+i;}
inline void cal(int x,int u){
	for(int re i=1;i<=len[u];i++)
		ans+=f[get_f(u,i-1)]*g[get_g(x,i)];
	for(int re i=1;i<=len[u];i++)
		ans+=g[get_g(u,i)]*f[get_f(x,i-1)];
	for(int re i=0;i<=len[u]-1;i++)
		g[get_g(x,i)]+=g[get_g(u,i+1)];
	for(int re i=1;i<=len[u];i++)
		g[get_g(x,i)]+=f[get_f(x,i)]*f[get_f(u,i-1)],f[get_f(x,i)]+=f[get_f(u,i-1)];
}
inline void sol(int u){
	if(!lson[u]){f[get_f(u,0)]=1,g[get_g(u,0)]=0;return;}
	sol(lson[u]);
	g[get_g(u,len[u])]=g[get_g(u,len[u]-1)]=0;
	ans+=g[get_g(u,0)],f[get_f(u,0)]=1;
	for(int re i=first[u];i;i=e[i].nxt){
		int re v=e[i].v;
		if(v==fa[u]||v==lson[u])continue;
		sol(v),cal(u,v);
	}
}
signed main(){
//	freopen("hothotels.in","r",stdin);
//	freopen("hothotels.out","w",stdout);
	n=rd();
	for(int re i=1;i<n;i++){int re x=rd(),y=rd();add(x,y),add(y,x);}
	dep[1]=1,dfs1(1),top[1]=1,pos[1]=len[1],li=len[1]<<1,dfs2(1),sol(1);
	write(ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值