动态点分治

关于动态点分治

首先你要先会点分治,然后动态点分治就是把点分治可持久化一下,让其不用再每次询问时都重新做一遍
具体就是,你考虑做点分的时候,你在每个分治重心上统计它所管辖的所有点的信息,并计算答案。那么每个点的信息只会出现在它上面的分治重心中,所以我们把每层分治重心向上一层的连边,这样每个点的信息只会出现都在它所有的祖先中,然后你维护需要的信息,每次修改就往祖先跳,由于点分治只有\(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;
}

posted @ 2018-06-25 20:22  dyx_diversion  阅读(239)  评论(0编辑  收藏  举报