动态点分治
关于动态点分治
首先你要先会点分治,然后动态点分治就是把点分治可持久化一下,让其不用再每次询问时都重新做一遍
具体就是,你考虑做点分的时候,你在每个分治重心上统计它所管辖的所有点的信息,并计算答案。那么每个点的信息只会出现在它上面的分治重心中,所以我们把每层分治重心向上一层的连边,这样每个点的信息只会出现都在它所有的祖先中,然后你维护需要的信息,每次修改就往祖先跳,由于点分治只有\(log\)层,所以这颗点分树高度只有\(log\),可以接受。
维护的信息根据每到题变化,非常灵活。
例题:
ZJOI2007捉迷藏
题意:有一个\(n\)个点的树,初始都是黑点,现在有两种操作,一种是将一个点反色,另一种是询问树上最远的两个黑点的距离。
题解:
求树上最长链我们可以使用点分解决,对于每个分治重心,我们在它所管辖的子树里各找一条到分治重心的最长链,将最长链和次长链拼起来跟新答案。
现在我们考虑维护一下,对每个点开两个支持删除和取最大值的数据结构,一个存它管辖的所有点到它上一级分治重心的距离(我们称为A),就是一条一条的链。另一个存它所有下一级的分治重心到它的最长链(我们称为B),然后答案就是最长链和次长链的最大值,这个答案也要开个数据结构来维护
关于这个数据结构,\(multiset\)可以,但是好像常数巨大,我们使用两个堆来支持就行了,具体来说就是一个存数据,另一个存要删除的,取最大值的时候,如果两个堆队首一样就都弹掉
关于修改,例如开灯,也就是这个点不能用,对于他自己,把B里面的中的\(0\)给去掉,防止答案是一条以它为端点的链,对于它每个点分树上的父亲\(y\),从\(y\)的A中删去这条链长,再处理一下这次变动对\(fa[y]\)的B的影响即可
每次改动\(B\)时,要先将答案数组中他的贡献删掉,改完再加回去。
根本不能看的代码(下面还有一道例题)
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=1e5+5;
template<typename T>bool cmax(T &a,T b){return (a<b)?a=b,1:0;}
template<typename T>bool cmin(T &a,T b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
T ans=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
return ans*f;
}
template<typename T>void write(T x,char y)
{
if(x==0)
{
putchar('0'),putchar(y);
return;
}
if(x<0)
{
putchar('-');
x=-x;
}
static char wr[20];
int top=0;
for(;x;x/=10)wr[++top]=x%10+'0';
while(top)putchar(wr[top--]);
putchar(y);
}
void file()
{
#ifndef ONLINE_JUDGE
freopen("hard.in","r",stdin);
freopen("hard.out","w",stdout);
#endif
}
int n;
vector<int>E[N];
#define pb push_back
void input()
{
int x,y;
n=read<int>();
For(i,2,n)
{
x=read<int>(),y=read<int>();
E[x].pb(y),E[y].pb(x);
}
}
typedef priority_queue<int> Q;
struct PQ
{
Q q,del;
void push(int x){q.push(x);}
void erase(int x){del.push(x);}
int top()
{
while(!del.empty()&&del.top()==q.top())
{del.pop(),q.pop();}
return q.top();
}
void pop()
{
while(!del.empty()&&del.top()==q.top())
{del.pop(),q.pop();}
q.pop();
}
int sec()
{
int y=top(),res;
pop();
res=y+top();
push(y);
return res;
}
int size(){return q.size()-del.size();}
}A[N],B[N],ans;
//multiset<int>A[N],B[N],ans;
const int inf=0x3f3f3f3f;
int fa[N],size[N],rt,Min,sum;
bool ban[N];
void get_root(int u,int pre)
{
int v,Max=0;
size[u]=1;
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(v==pre||ban[v])continue;
get_root(v,u);
size[u]+=size[v];
cmax(Max,size[v]);
}
cmax(Max,sum-size[u]);
if(cmin(Min,Max))rt=u;
}
void get_dep(int u,int pre,int dis)
{
//cout<<u<<' '<<dis<<' '<<rt<<' '<<fa[rt]<<endl;
// cout<<"A"<<' '<<rt<<' '<<dis<<' '<<u<<endl;
int v;
A[rt].push(dis);
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(v==pre||ban[v])continue;
get_dep(v,u,dis+1);
}
}
void insert(PQ &s)
{
if(s.size()>1)
{
// cout<<"C"<<' '<<s.sec()<<endl;
ans.push(s.sec());
}
}
void erase(PQ s)
{
if(s.size()>1)
{
// cout<<"D"<<' '<<s.sec()<<endl;
ans.erase(s.sec());
}
}
void solve(int u)
{
int v;
ban[u]=1;
//cerr<<u<<endl;
B[u].push(0);
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(ban[v])continue;
//cerr<<u<<' '<<v<<endl;;
sum=Min=size[v];
get_root(v,u);
fa[rt]=u;
get_dep(v,u,1);
// cout<<"B"<<' '<<u<<' '<<A[rt].top()<<endl;
//cout<<u<<' '<<rt<<endl;
//B[u].insert(fir(A[rt]));
//cout<<fir(A[rt])<<endl;
//B[u].insert(fir(A[u]));
//int res=fir(A[rt]);
//cerr<<u<<' '<<res<<endl;
B[u].push(A[rt].top());
//cerr<<u<<' '<<res<<endl;
solve(rt);
}
//cerr<<endl;
insert(B[u]);
}
int dep[N],st[N][21],Log[N];
void dfs(int u,int pre)
{
int v;
dep[u]=dep[pre]+1;
st[u][0]=pre;
For(i,1,Log[dep[u]])st[u][i]=st[st[u][i-1]][i-1];
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(v==pre)continue;
dfs(v,u);
}
}
void init()
{
For(i,1,n)Log[i]=Log[i>>1]+1;
dfs(1,0);
rt=0;Min=sum=n;
get_root(1,0);
get_dep(1,0,1);
solve(rt);
// For(i,1,n)cout<<fa[i]<<' ';
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
int s=dep[x]-dep[y];
Fordown(i,Log[s],0)
if((s>>i)&1)x=st[x][i];
if(x==y)return x;
s=dep[x];
Fordown(i,Log[s],0)
{
if(st[x][i]^st[y][i])
x=st[x][i],y=st[y][i];
}
return st[x][0];
}
int Dis(int x,int y){return dep[x]+dep[y]-dep[LCA(x,y)]*2;}
char opt[20];
int m,cl[N],cnt;
void on(int x)
{
// cerr<<B[x].size()<<endl;
int t1,t2;
t1=B[x].size()>1?B[x].sec():-1;
//erase(B[x]);
//cout<<endl;
//cout<<x<<endl;
//for(it=ans.rbegin();it!=ans.rend();++it)cout<<*it<<' ';
//cout<<endl<<endl;;
B[x].erase(0);
t2=B[x].size()>1?B[x].sec():-1;
if(t1!=t2)
{
if(t1!=-1)ans.erase(t1);
if(t2!=-1)ans.push(t2);
}
//insert(B[x]);
//B[x].erase(B[x].lower_bound(0));
//cerr<<x<<endl<<endl;;
for(int y=x;fa[y];y=fa[y])
{
// cout<<fa[y]<<' '<<B[fa[y]].size()<<endl;
t1=B[fa[y]].size()>1?B[fa[y]].sec():-1;
//erase(B[fa[y]]);
if(A[y].size())B[fa[y]].erase(A[y].top());
A[y].erase(Dis(x,fa[y]));
// cout<<"Dis"<<' '<<Dis(x,fa[y])<<endl;
if(A[y].size())B[fa[y]].push(A[y].top());
//insert(B[fa[y]]);
t2=B[fa[y]].size()>1?B[fa[y]].sec():-1;
if(t1!=t2)
{
if(t1!=-1)ans.erase(t1);
if(t2!=-1)ans.push(t2);
}
}
}
void off(int x)
{
int t1,t2;
t1=B[x].size()>1?B[x].sec():-1;
//erase(B[x]);
B[x].push(0);
//insert(B[x]);
t2=B[x].size()>1?B[x].sec():-1;
if(t1!=t2)
{
if(t1!=-1)ans.erase(t1);
if(t2!=-1)ans.push(t2);
}
for(int y=x;fa[y];y=fa[y])
{
//erase(B[fa[y]]);
t1=B[fa[y]].size()>1?B[fa[y]].sec():-1;
if(A[y].size())B[fa[y]].erase(A[y].top());
A[y].push(Dis(x,fa[y]));
// cout<<"Dis"<<' '<<Dis(x,fa[y])<<endl;
if(A[y].size())B[fa[y]].push(A[y].top());
//insert(B[fa[y]]);
t2=B[fa[y]].size()>1?B[fa[y]].sec():-1;
if(t1!=t2)
{
if(t1!=-1)ans.erase(t1);
if(t2!=-1)ans.push(t2);
}
}
}
void work()
{
//For(i,1,n)cout<<fa[i]<<' ';
//write(fir(ans),'\n');
int x;
cnt=n;
m=read<int>();
For(i,1,m)
{
scanf("%s",opt);
if(opt[0]=='C')
{
x=read<int>();
if(!cl[x])
{
cl[x]=1;
cnt--;
on(x);
}
else
{
cl[x]=0;
cnt++;
off(x);
}
}
else
{
if(cnt<=1)write(cnt-1,'\n');
else write(ans.top(),'\n');
}
// cout<<endl;
}
}
int main()
{
file();
input();
init();
work();
return 0;
}
BZOJ3730
题意:一颗\(n\)个点的树,每个点有点权,要求支持修改一个点权和查询距离某个点距离不超过\(k\)的点的点权和
题解:
每个点开两个树状数组,分别维护距它不超过多少的点权之和,和它子树中距离它父亲不超过多少的点权之和
这样在询问时对于每一个分治重心\(y\),贡献就是不超过\(k-dis(x,y)\)的点权和减去不超过\(dis(x,fa[y])\)的点权和,减去的这部分会在他的父亲被算回来。
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=1e5+5;
template<typename T>bool cmax(T &a,T b){return (a<b)?a=b,1:0;}
template<typename T>bool cmin(T &a,T b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
T ans=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
return ans*f;
}
template<typename T>void write(T x,char y)
{
if(x==0)
{
putchar('0'),putchar(y);
return;
}
if(x<0)
{
putchar('-');
x=-x;
}
static char wr[20];
int top=0;
for(;x;x/=10)wr[++top]=x%10+'0';
while(top)putchar(wr[top--]);
putchar(y);
}
void file()
{
#ifndef ONLINE_JUDGE
freopen("3730.in","r",stdin);
freopen("3730.out","w",stdout);
#endif
}
int n,m;
int val[N];
vector<int>E[N];
#define pb push_back
void input()
{
int x,y;
n=read<int>(),m=read<int>();
For(i,1,n)val[i]=read<int>();
For(i,2,n)
{
x=read<int>(),y=read<int>();
E[x].pb(y),E[y].pb(x);
}
}
bool ban[N];
int rt,Min,sum,size[N];
void get_root(int u,int pre)
{
int v,Max=0;
size[u]=1;
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(v==pre||ban[v])continue;
get_root(v,u);
size[u]+=size[v];
cmax(Max,size[v]);
}
cmax(Max,sum-size[u]);
if(cmin(Min,Max))rt=u;
}
int fa[N][21],cnt[N],dis[N][21];
vector<int>s1[N],s2[N];
void get_dep(int u,int pre,int G,int Dis)
{
int v;
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(v==pre||ban[v])continue;
++cnt[v];
fa[v][cnt[v]]=G,dis[v][cnt[v]]=Dis+1;
get_dep(v,u,G,Dis+1);
}
}
int sz;
void solve(int u)
{
//cerr<<u<<' '<<sum<<endl;
s1[u].resize(sum+1),s2[u].resize(sum+1);
// cout<<s1[u].size()-1<<endl;
int v,all=sum;
ban[u]=1;
get_dep(u,u,u,0);
For(i,0,E[u].size()-1)
{
v=E[u][i];
if(ban[v])continue;
// cerr<<u<<' '<<v<<endl;
//get_dep(v,u,u,1i);
//cerr<<size[v]<<' '<<size[u]<<' '<<sum<<endl;
sum=Min=(size[v]>size[u]?all-size[u]:size[v]);
get_root(v,u);
//cout<<rt<<' '<<sum<<' '<<Min<<endl;
solve(rt);
}
}
void init()
{
sum=Min=n;
get_root(1,0);
solve(rt);
}
void add(vector<int> &s,int x,int v)
{
//cerr<<x<<' '<<limte<<endl;
int limte=s.size()-1;
for(;x&&x<=limte;x+=x&-x)s[x]+=v;
}
int cal(vector<int> &s,int x)
{
cmin(x,(int)s.size()-1);
int res=0;
for(;x;x-=x&-x)res+=s[x];
return res;
}
void insert(int x,int v)
{
// if(!x||x>n)return;
// cerr<<dis[x][cnt[x]]<<' '<<x<<' '<<fa[x][cnt[x]]<<endl;
if(!x||x>n)return;
add(s2[x],dis[x][cnt[x]],v);
// for(int j=dis[x][cnt[x]];j&&j<s2[x].size();j+=j&-j)s2[x][j]+=v;
Fordown(i,cnt[x],1)
{
// cerr<<fa[x][i]<<' '<<s1[fa[x][i]].size()-1<<' '<<dis[x][i]<<' '<<dis[x][i-1]<<endl;
add(s1[fa[x][i]],dis[x][i],v);
add(s2[fa[x][i]],dis[x][i-1],v);
// for(int j=dis[x][i];j&&j<s1[fa[x][i]].size();j+=j&-j)s1[fa[x][i]][j]+=v;
// for(int j=dis[x][i-1];j&&j<s1[fa[x][i]].size();j+=j&-j)s2[fa[x][i]][j]+=v;
}
}
int query(int x,int k)
{
// if(!x||x>n)return 0;
int res=cal(s1[x],k)+val[x];
// cerr<<res<<endl;
Fordown(i,cnt[x],1)if(dis[x][i]<=k)
{
res+=val[fa[x][i]]+cal(s1[fa[x][i]],k-dis[x][i])-cal(s2[fa[x][i+1]],k-dis[x][i]);
// cerr<<fa[x][i]<<' '<<res<<endl;
}
return res;
}
int lans;
void work()
{
// For(i,1,n)cout<<fa[i][cnt[i]]<<' ';puts("");
int opt,x,y;
//For(i,1,n)cout<<i<<' '<<(int)s1[i].size()<<endl;
//For(i,1,n)For(j,1,cnt[i])cout<<fa[i][j]<<' '<<dis[i][j]<<' '<<(j==cnt[i]?'\n':' ');
For(i,1,n)insert(i,val[i]);
For(i,1,n)fa[i][cnt[i]+1]=i;
while(m--)
{
opt=read<int>(),x=read<int>()^lans,y=read<int>()^lans;
if(opt==0)lans=query(x,y),write(lans,'\n');
else insert(x,y-val[x]),val[x]=y;
}
}
int main()
{
file();
input();
init();
work();
return 0;
}