【bzoj4372】爍爍的遊戲
Portal -->bzoj4372
Solution
感覺自己動態點分治好像沒有學好qwq今天借這題來補個檔qwq
其實所謂的動態點分治大概就是。。和點分一樣的套路,但是不同的是我們要更進一步利用好每次找重心而產生的優秀性質:深度期望\(log\)
我們考慮按照點分治那樣每次找重心,但是不同的是我們要記錄當前層遞歸的前一層的重心(記為\(pre[rt]=fa\),其中\(fa\)是上一層遞歸找到的重心,\(rt\)是當前層找到的重心)
然後每個重心會有一個管轄範圍(具體一點就是\(rt\)是在哪個範圍內為重心),我們考慮將整個管轄範圍內點的對應信息記錄到這個重心上(在不同的題裏面記錄方式不同,比如說這題的話就是對於每一個重心開一棵線段樹或者樹狀數組來記錄)
然後每次查詢的話(比如說查詢點\(x\)),我們就從\(x\)開始,每次跳到上一層遞歸的重心,調用重心中存儲的與\(x\)有關的值,直到根,因為建點分樹的方式是每層找重心,所以期望深度是\(log\)級別的
修改的話同理(比如說修改點\(x\)),也是從\(x\)開始,每次跳到上一層遞歸的重心,修改重心中存儲的與\(x\)有關的值,直到根,同樣也是\(log\)級別的
聽起來很暴力
但是。。點分治本來不就是一個讓你的暴力跑得飛快的黑科技嗎哈哈哈哈哈
回到這道題
這題中我們需要維護的顯然是每個點的權值,考慮在每個重心處維護的信息是管轄範圍內每個點修改了多少(類似打了個修改標記一樣),然後因為修改與這個點本身的\(dep\)
這樣有一個好處,就是修改的時候比較方便操作。我們考慮在一開始建點分樹的時候,將每個重心的管轄範圍內的每個點排完序之後的順序記錄下來(將這個數組記為\(id\)),同時記錄排完序之後的到重心的距離(將這個數組記為\(rec\)),那麽我們只需要在這個記錄距離的\(rec\)數組裏二分一下,找到滿足題目條件的最靠後的那個點(其實就是直接upper_bound一下就好了)在記錄數組中的下標\(pos\),然後將區間\([1,pos]\)中的點值全部加上\(w\)
這裏需要註意的是,因為我們在\(rec\)數組中存的是到當前層重心的距離,所以每次在\(rec\)中二分的時候帶進去的查詢值應該要減去目標點到當前層重心的距離
接下來還有一個小問題,就是由於當前重心\(rt\)的管轄範圍也在上一層重心的管轄範圍內,所以在往上跳修改的時候可能會有一部分的點被修改了兩次,那麽這個時候我們需要小小的容斥一下,具體一點的話就是:假設我們當前的修改操作是\(M\ x\ d\ w\),當前層的重心是\(rt\),我們要將\(pre[rt]\)中與\(rt\)中計算重復的部分減去,那麽我們應該將\(rec\)值\(\in [0,d-dis(pre[rt],x)-1]\)的這些點在\(rt\)這層的修改值去掉,因為在\(pre[rt]\)這層會修改到(然而實際操作起來的時候我們可以開兩棵線段樹(或者樹狀數組),一棵記錄加的標記,一棵記錄容斥要減掉的標記)
最後就是查詢,查詢相對來說就會沒那麽繁瑣了,因為在修改中我們已經容斥過了,所以查詢的時候直接從目標點開始一直往根跳然後每次將該層的重心中記錄的標記累加進去就好了
? 不過如果寫線段樹的話。。空間和時間都會相對來說吃緊一點(特別對於我這種辣雞自帶常數的選手qwq)所以保險一點還是寫樹狀數組吧反正。。這題是區間修改單點查詢嘛問題不大(所以我到底是怎麽做到明明寫的是樹狀數組還寫了那麽長的qwq)
然而實際上。。貌似寫線段樹的話如果改成單點修改區間查詢這樣的方式(其實就是樹狀數組那種套路),不需要標記下傳之類的也能做到常數十分優秀並且代碼簡潔
代碼大概長這個樣子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define TOP 20
#define vct vector<int>
using namespace std;
const int N=1e5+10,BIT=N*17*2;
struct xxx{
int y,nxt;
}a[N*2];
char s[10];
vct rec[N][2];
int f[N][TOP+1],dep[N],vis[N];
int h[N],sz[N],mx[N],belong[N],tmprec[N],tmpdep[N];
int df_dep[N],pre[N],df_t[N],df_sz[N];
int id[TOP+1][N][2];
int n,m,tot,rt,rt_sz,ans;
namespace Bit{/*{{{*/
int c[BIT],st[N][2],mx[N][2];
int tot=0;
void _update(int which,int x,int delta,int op){
for (;x<=mx[which][op];x+=x&-x)
c[st[which][op]+x]+=delta;
}
void update(int which,int op,int l,int r,int delta){
_update(which,l,delta,op);
if (r<mx[which][op]) _update(which,r+1,-delta,op);
}
int query(int which,int op,int x){
int ret=0;
for (;x;x-=x&-x) ret+=c[st[which][op]+x];
return ret;
}
}/*}}}*/
//orignal tree{{{
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void predfs(int fa,int x,int d){
int u;
dep[x]=d; f[x][0]=fa;
for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
predfs(x,u,d+1);
}
}
int get_lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int i=TOP;i>=0;--i)
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=TOP;i>=0;--i)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int get_dis(int x,int y){
int lca=get_lca(x,y);
return dep[x]+dep[y]-2*dep[lca];
}
/*}}}*/
//get_rt{{{
void get_sz(int fa,int x){
int u;
sz[x]=1; mx[x]=0;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_sz(x,u);
sz[x]+=sz[u];
if (sz[u]>mx[x]) mx[x]=sz[u];
}
}
void get_rt(int Rt,int fa,int x){
int u;
mx[x]=max(mx[x],sz[Rt]-sz[x]);
if (mx[x]<=rt_sz) rt=x,rt_sz=mx[x];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_rt(Rt,x,u);
}
}/*}}}*/
//df-tree{{{
void dfs(int fa,int x,int d){
int u;
tmprec[++tmprec[0]]=x; tmpdep[x]=d;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
dfs(x,u,d+1);
}
}
bool cmp(int x,int y){return tmpdep[x]<tmpdep[y];}
void get_lis(int x,int rt,int which_t,int op){
tmprec[0]=0;
Bit::st[rt][op]=Bit::tot;
if (op==0)
dfs(0,rt,0),Bit::mx[rt][op]=tmprec[0];
else
dfs(0,x,0),Bit::mx[rt][op]=Bit::mx[rt][0];
Bit::tot+=tmprec[0];
sort(tmprec+1,tmprec+1+tmprec[0],cmp);
rec[rt][op].resize(tmprec[0]);
for (int i=1;i<=tmprec[0];++i){
id[which_t][tmprec[i]][op]=i;
rec[rt][op][i-1]=tmpdep[tmprec[i]];
}
}
void solve(int fa,int x,int Sz){
int u,Rt;
rt=0,rt_sz=n;
get_sz(0,x);
get_rt(x,0,x);
Rt=rt;
df_t[Rt]=df_t[fa]+1; df_sz[Rt]=Sz;
pre[Rt]=fa;
get_lis(x,Rt,df_t[Rt],0);
get_lis(x,Rt,df_t[Rt],1);
vis[Rt]=true;
for (int i=h[Rt];i!=-1;i=a[i].nxt){
u=a[i].y;
if (vis[u]) continue;
solve(Rt,u,sz[u]>sz[Rt]?Sz-sz[Rt]:sz[u]);
}
}
void modify(int x,int aim,int d,int delta){
int dis=d-get_dis(x,aim),pos;
pos=upper_bound(rec[x][0].begin(),rec[x][0].end(),dis)-rec[x][0].begin();
Bit::update(x,0,1,pos,delta);
if (pre[x]){
dis=d-get_dis(pre[x],aim)-1;
if (dis>=0){
pos=upper_bound(rec[x][1].begin(),rec[x][1].end(),dis)-rec[x][1].begin();
Bit::update(x,1,1,pos,-delta);
}
modify(pre[x],aim,d,delta);
}
}
int query(int x,int aim){
int ret=Bit::query(x,0,id[df_t[x]][aim][0]);
if (pre[x]){
ret+=Bit::query(x,1,id[df_t[x]][aim][1]);
ret+=query(pre[x],aim);
}
return ret;
}
/*}}}*/
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,d,w;
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
predfs(0,1,1);
solve(0,1,n);
for (int i=1;i<=m;++i){
scanf("%s",s);
if (s[0]=='Q'){
scanf("%d",&x);
ans=query(x,x);
printf("%d\n",ans);
}
else{
scanf("%d%d%d",&x,&d,&w);
modify(x,x,d,w);
}
}
}
【bzoj4372】爍爍的遊戲