BZOJ3730 震波+BZOJ4372 爍爍的遊戲(動態點分治)

震波

在一片土地上有N個城市,通過N-1條無向邊互相連接,形成一棵樹的結構,相鄰兩個城市的距離爲1,其中第i個城市的價值爲value[i]。
不幸的是,這片土地常常發生地震,並且隨着時代的發展,城市的價值也往往會發生變動。
接下來你需要在線處理M次操作:
0 x k 表示發生了一次地震,震中城市爲x,影響範圍爲k,所有與x距離不超過k的城市都將受到影響,該次地震造成的經濟損失爲所有受影響城市的價值和。
1 x y 表示第x個城市的價值變成了y。
爲了體現程序的在線性,操作中的x、y、k都需要異或你程序上一次的輸出來解密,如果之前沒有輸出,則默認上一次的輸出爲0。

Input

第一行包含兩個正整數N和M。
第二行包含N個正整數,第i個數表示value[i]。
接下來N-1行,每行包含兩個正整數u、v,表示u和v之間有一條無向邊。
接下來M行,每行包含三個數,表示M次操作。

Output

包含若干行,對於每個詢問輸出一行一個正整數表示答案。

Sample Input

8 1 1 10 100 1000 10000 100000 1000000 10000000 1 2 1 3 2 4 2 5 3 6 3 7 3 8 0 3 1

Sample Output

11100101

Hint

 

1<=N,M<=100000

1<=u,v,x<=N

1<=value[i],y<=10000

0<=k<=N-1

 

 

 

 

 

題解

首先想到動態點分治+線段樹

我們這時顯然不能維護dfs序上的權值

我們可以對到當前點分中心的距離來開一個線段樹

如果當前點分中心爲u

那麼線段樹需要維護距離點u爲 i 的所有點的權值之和

但是我們可能會算重,有可能點分中心的父親會再一次計算某一些點的點權和

所有我們考慮如何去重

我們可以對點 u 再用一個線段樹維護它的子樹節點到它的點分樹父親的距離爲 i 的點權和

在查詢的時候減掉會被父親計算的點的點權和即可

(強制在線,RE一般就是WA了,當然也有可能是真RE了)

代碼:(查出來無數個sb錯,還是一直RE,最後發現自己的動態開點線段樹沒有判等。。。)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num;
}
#define N 100005
#define LOG 17
#define lc a[i].l,l,mid
#define rc a[i].r,mid+1,r
const int INF=0x3f3f3f3f;
int n,m;
int fir[N],to[2*N],nxt[2*N],cnt;
int val[N];
int tmpsiz[N],nrt,all;bool vis[N];
int dis[LOG][N],dfa[N],dep[N];
struct node{int l,r,x;}a[N*66];
int T1[N],T2[N],tot;
inline void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
void insert(int &i,int l,int r,int x,int k)
{
	if(!i)i=++tot;
	a[i].x+=k;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(x<=mid)insert(lc,x,k);
	else insert(rc,x,k);
}
int query(int i,int l,int r,int qr)
{
	if(!i)return 0ll;
	if(l==r)return a[i].x;
	int mid=(l+r)>>1;
	if(qr<=mid)return query(lc,qr);
	else return a[a[i].l].x+query(rc,qr);
}
void findrt(int u,int ff)
{
	int mx=0;tmpsiz[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		if(!vis[v=to[p]]&&v!=ff){
			findrt(v,u);
			tmpsiz[u]+=tmpsiz[v];
			mx=max(mx,tmpsiz[v]);
		}
	}
	mx=max(mx,all-tmpsiz[u]);
	if(2*mx<=all)nrt=u;
}
inline int getrt(int u,int sz)
{
	nrt=-INF;all=sz;
	findrt(u,0);return nrt;
}
void pre(int u,int ff,int d,int rt)
{
	insert(T1[rt],0,n-1,dis[d][u],val[u]);
	if(dfa[rt])insert(T2[rt],0,n-1,dis[d-1][u],val[u]);//
	tmpsiz[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		if(!vis[v=to[p]]&&v!=ff){
			dis[d][v]=dis[d][u]+1;
			pre(v,u,d,rt);
			tmpsiz[u]+=tmpsiz[v];
		}
	}
}
void DFZ(int u,int d)
{
	vis[u]=1;dep[u]=d;
	dis[d][u]=0;pre(u,0,d,u);
	for(int v,p=fir[u];p;p=nxt[p]){
		if(!vis[v=to[p]]){
			dfa[v=getrt(v,tmpsiz[v])]=u;
			DFZ(v,d+1);
		}
	}
}
inline void modify(int u,int k)
{
	for(int t=u;t;t=dfa[t]){
		insert(T1[t],0,n-1,dis[dep[t]][u],k-val[u]);//
		if(dfa[t])insert(T2[t],0,n-1,dis[dep[t]-1][u],k-val[u]);//
	}
	val[u]=k;
}
inline int query(int u,int k)
{
	long long ans=0;
	for(int t=u;t;t=dfa[t]){
		if(dfa[t]&&dis[dep[t]-1][u]<=k) ans-=query(T2[t],0,n-1,k-dis[dep[t]-1][u]);//
		if(dis[dep[t]][u]<=k) ans+=query(T1[t],0,n-1,k-dis[dep[t]][u]);//
	}
	return int(ans);
}
int main()
{
	int i,op,u,v;int ans=0;
	n=gi();m=gi();
	for(i=1;i<=n;i++)val[i]=gi();
	for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
	DFZ(getrt(1,n),0);
	for(i=1;i<=m;i++){
		op=gi();u=gi();v=gi();
		u^=ans;v^=ans;
		if(op)modify(u,v);
		else printf("%d\n",ans=query(u,v));
	}
}

 

 

 

 

 

 

爍爍的遊戲

背景:爍爍很喜歡爬樹,這嚇壞了樹上的皮皮鼠。
題意:
給定一顆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<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define LOG 17
const int INF=0x3f3f3f3f;
int n,m;
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int tmpsiz[N],nrt,all;bool vis[N];
int dis[LOG][N],dfa[N],dep[N];
void findrt(int u,int ff)
{
	int mx=0;tmpsiz[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		if(!vis[v=to[p]]&&v!=ff){
			findrt(v,u);
			tmpsiz[u]+=tmpsiz[v];
			mx=max(mx,tmpsiz[v]);
		}
	}
	mx=max(mx,all-tmpsiz[u]);
	if(mx+mx<=all)nrt=u;
}
int getrt(int u,int sz)
{
	nrt=-INF;all=sz;
	findrt(u,0);return nrt;
}
#define lc a[i].l
#define rc a[i].r
struct node{int l,r,x;}a[N<<7];
int T1[N],T2[N],tot;
void insert(int &i,int l,int r,int x,int k)
{
	if(!i)i=++tot;a[i].x+=k;if(l==r)return;
	int mid=(l+r)>>1;
	if(x<=mid)insert(lc,l,mid,x,k);
	else insert(rc,mid+1,r,x,k);
}
int query(int i,int l,int r,int ql)
{
	if(!i)return 0;
	if(l==r)return a[i].x;
	int mid=(l+r)>>1;
	if(ql<=mid)return query(lc,l,mid,ql)+a[rc].x;
	else return query(rc,mid+1,r,ql);
}
void pre(int u,int ff,int d)
{
	tmpsiz[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		if(!vis[v=to[p]]&&v!=ff){
			dis[d][v]=dis[d][u]+1;
			pre(v,u,d);
			tmpsiz[u]+=tmpsiz[v];
		}
	}
}
void DFZ(int u,int d)
{
	vis[u]=1;dep[u]=d;
	pre(u,0,d);
	for(int v,p=fir[u];p;p=nxt[p]){
		if(!vis[v=to[p]]){
			dfa[v=getrt(v,tmpsiz[v])]=u;
			DFZ(v,d+1);
		}
	}
}
inline void modify(int u,int k,int s)
{
	for(int t=u;t;t=dfa[t]){
		if(dis[dep[t]][u]<=k) insert(T1[t],0,n,k-dis[dep[t]][u],s);//
		if(dfa[t]&&dis[dep[t]-1][u]<=k) insert(T2[t],0,n,k-dis[dep[t]-1][u],s);//
	}
}
inline int query(int u)
{
	long long ans=0;
	for(int t=u;t;t=dfa[t]){
		ans+=query(T1[t],0,n,dis[dep[t]][u]);//
		if(dfa[t]) ans-=query(T2[t],0,n,dis[dep[t]-1][u]);//
	}
	return int(ans);
}
char op[3];
int main()
{
	int i,u,v,s;
	n=gi();m=gi();
	for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
	DFZ(getrt(1,n),0);
	for(i=1;i<=m;i++){
		scanf("%s",op);
		if(op[0]=='M'){
			u=gi();v=gi();s=gi();
			modify(u,v,s);
		}
		else printf("%d\n",query(gi()));
	}
}

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章