1. 程式人生 > >【BZOJ4372】爍爍的遊戲 動態樹分治+線段樹

【BZOJ4372】爍爍的遊戲 動態樹分治+線段樹

printf esp ans 當前 sam 註意 與他 dfs 接下來

【BZOJ4372】爍爍的遊戲

Description

背景:爍爍很喜歡爬樹,這嚇壞了樹上的皮皮鼠。
題意:
給定一顆n個節點的樹,邊權均為1,初始樹上沒有皮皮鼠。
爍爍他每次會跳到一個節點u,把周圍與他距離不超過d的節點各吸引出w只皮皮鼠。皮皮鼠會被爍爍吸引,所以會一直待在節點上不動。
爍爍很好奇,在當前時刻,節點u有多少個他的好朋友---皮皮鼠。
大意:
給一顆n個節點的樹,邊權均為1,初始點權均為0,m次操作:
Q x:詢問x的點權。
M x d w:將樹上與節點x距離不超過d的節點的點權均加上w。

Input

第一行兩個正整數:n,m
接下來的n-1行,每行三個正整數u,v,代表u,v之間有一條邊。

接下來的m行,每行給出上述兩種操作中的一種。

Output

對於每個Q操作,輸出當前x節點的皮皮鼠數量。

Sample Input

7 6
1 2
1 4
1 5
2 3
2 7
5 6
M 1 1 2
Q 5
M 2 2 3
Q 3
M 1 2 1
Q 2

Sample Output

2
3
6

HINT

數據範圍:
n,m<=10^5,|w|<=10^4
註意:w不一定為正整數,因為爍爍可能把皮皮鼠嚇傻了。

題解:動態點分治+線段樹裸題,每個節點開兩棵線段樹維護它子樹中的以及它的父親要從它中減去的即可。

註意特判邊界的問題。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=100010;
int n,m,tot,ans,cnt,mn,rt;
struct sag
{
	int ls,rs,sum;
}s[maxn*150];
int to[maxn<<1],next[maxn<<1],head[maxn],siz[maxn],dep[maxn],pos[maxn],md[20][maxn<<1],fa[maxn],Log[maxn<<1];
int r1[maxn],r2[maxn];
bool vis[maxn];
char str[5];
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void getrt(int x,int fa)
{
	int i,tmp=0;
	siz[x]=1;
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
	tmp=max(tmp,tot-siz[x]);
	if(tmp<mn)	mn=tmp,rt=x;
}
void solve(int x)
{
	vis[x]=1;
	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])
		tot=siz[to[i]],mn=1<<30,getrt(to[i],x),fa[rt]=x,solve(rt);
}
void dfs(int x,int fa)
{
	md[0][++pos[0]]=dep[x],pos[x]=pos[0];
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa)
		dep[to[i]]=dep[x]+1,dfs(to[i],x),md[0][++pos[0]]=dep[x];
}
inline int getmin(int a,int b)
{
	a=pos[a],b=pos[b];
	if(a>b)	swap(a,b);
	int k=Log[b-a+1];
	return min(md[k][a],md[k][b-(1<<k)+1]);
}
inline int dis(int a,int b)
{
	return dep[a]+dep[b]-2*getmin(a,b);
}
void updata(int l,int r,int &x,int a,int b)
{
	if(a<0)	return ;
	if(!x)	x=++tot;
	s[x].sum+=b;
	if(l==r)	return ;
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,s[x].ls,a,b);
	else	updata(mid+1,r,s[x].rs,a,b);
}
int query(int l,int r,int x,int a,int b)
{
	if(!x||(a<=l&&r<=b))	return s[x].sum;
	int mid=(l+r)>>1;
	if(b<=mid)	return	query(l,mid,s[x].ls,a,b);
	if(a>mid)	return	query(mid+1,r,s[x].rs,a,b);
	return query(l,mid,s[x].ls,a,b)+query(mid+1,r,s[x].rs,a,b);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,j,x,y,a,b,u;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	dfs(1,0),tot=n,mn=1<<30,getrt(1,0),solve(rt),tot=0;
	for(j=1;(1<<j)<=2*n-1;j++)	for(i=1;i+(1<<j)-1<=2*n-1;i++)	md[j][i]=min(md[j-1][i],md[j-1][i+(1<<(j-1))]);
	for(i=2;i<=2*n-1;i++)	Log[i]=Log[i>>1]+1;
	for(i=1;i<=m;i++)
	{
		scanf("%s",str);
		if(str[0]==‘Q‘)
		{
			u=rd(),ans=0;
			for(x=u;x;x=y)
			{
				y=fa[x];
				ans+=query(0,n,r1[x],dis(u,x),n);
				if(y)	ans-=query(0,n,r2[x],dis(u,y),n);
			}
			printf("%d\n",ans);
		}
		else
		{
			u=rd(),a=rd(),b=rd();
			for(x=u;x;x=y)
			{
				y=fa[x];
				updata(0,n,r1[x],min(n,a-dis(u,x)),b);
				if(y)	updata(0,n,r2[x],min(n,a-dis(u,y)),b);
			}
		}
	}
	return 0;
}//7 2 1 2 1 4 1 5 2 3 2 7 5 6 M 1 2 1 Q 2

【BZOJ4372】爍爍的遊戲 動態樹分治+線段樹