码迷,mamicode.com
首页 > 其他好文 > 详细

树链剖分

时间:2018-10-08 20:44:49      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:dig   个数   --   查询   cas   struct   stdout   fine   for   

将树分为若干条重链和轻链 ,再用线段树维护

模板https://www.luogu.org/problemnew/show/P3384

#include<bits/stdc++.h>
using namespace std;
#define ll long long
void FRE(){freopen(".in","r",stdin);freopen(".out","w",stdout);}
void FCL(){fclose(stdin);fclose(stdout);}
inline ll rd()
{
	ll x=0,ert=1;char lk=getchar();
	while(!isdigit(lk)){if(lk==‘-‘) ert=-1;lk=getchar();}
	while(isdigit(lk)){x=(x<<3)+(x<<1)+(lk-‘0‘);lk=getchar();}
	return x*ert;
}
const int N=1e5+10;
struct zx{int nx,to;}e[N<<1];
struct zy{int l,r,len,sum,fl;}s[N<<2];
int h[N],nm,sz[N],son[N],fa[N],seg[N],a[N],A[N],dep[N],top[N],P,cnt;
void ud(int p,int x) {s[p].sum+=((ll)s[p].len*(ll)x);s[p].fl+=x;s[p].sum%=P;s[p].fl%=P;}
void df(int x)
{
	dep[x]=dep[fa[x]]+(sz[x]=1);
	for(int i=h[x],y;i;i=e[i].nx)
	{
		if((y=e[i].to)==fa[x]) continue;
		fa[y]=x;df(y);sz[x]+=sz[y];
		if(sz[y]>sz[son[x]]) son[x]=y;
	}
}
void dfs(int x)
{
	a[seg[x]=++cnt]=A[x];
	if(!son[x]) return ;
	top[son[x]]=top[x];dfs(son[x]);
	for(int i=h[x],y;i;i=e[i].nx)
	{
		if((y=e[i].to)==fa[x]||y==son[x]) continue;
		top[y]=y;dfs(y);
	}
}
void biu(int p,int l,int r)
{
	s[p].l=l;s[p].r=r;
	if(l==r) {s[p].sum=a[l];s[p].len=1;return ;}
	biu(p<<1,l,(l+r)>>1);biu(p<<1|1,((l+r)>>1)+1,r);
	s[p].len=s[p<<1].len+s[p<<1|1].len;
	s[p].sum=s[p<<1].sum+s[p<<1|1].sum;s[p].sum%=P;
}
void add(int p,int l,int r,int x)
{
	if(l<=s[p].l&&r>=s[p].r) {ud(p,x);return ;}
	if(s[p].fl) ud(p<<1,s[p].fl),ud(p<<1|1,s[p].fl),s[p].fl=0;
	int mid=(s[p].l+s[p].r)>>1;
	if(l<=mid) add(p<<1,l,r,x);
	if(r>mid) add(p<<1|1,l,r,x);
	s[p].sum=s[p<<1].sum+s[p<<1|1].sum,s[p].sum%=P;
}
int wy(int p,int l,int r)
{
	if(l<=s[p].l&&r>=s[p].r)return s[p].sum;
	if(s[p].fl) ud(p<<1,s[p].fl),ud(p<<1|1,s[p].fl),s[p].fl=0;
	int mid=(s[p].l+s[p].r)>>1,ans=0;
	if(l<=mid) ans+=wy(p<<1,l,r);
	if(r>mid) ans+=wy(p<<1|1,l,r);
	s[p].sum=s[p<<1].sum+s[p<<1|1].sum,s[p].sum%=P;
	return ans%P;
}
int QA(int x,int y,int z)
{
	int fx=top[x],fy=top[y],ans=0;
	while(fx!=fy)
	{
		if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
		if(z) add(1,seg[fx],seg[x],z);
		else ans+=wy(1,seg[fx],seg[x]),ans%=P;
		x=fa[fx];fx=top[x];
	}
	if(dep[x]<dep[y]) swap(x,y);
	if(z) add(1,seg[y],seg[x],z);
	else ans+=wy(1,seg[y],seg[x]),ans%=P;
	return ans;
}
void Add(int x,int y){e[++nm].nx=h[x];h[x]=nm;e[nm].to=y;}
int main()
{
	int n=rd(),m=rd(),rt=rd();P=rd();
	for(int i=1;i<=n;i++) A[i]=rd();
	for(int i=1;i<n;i++)
	{
		int x=rd(),y=rd();
		Add(x,y);Add(y,x);
	}
	df(rt);top[rt]=rt;dfs(rt);biu(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int opt=rd(),x=rd();
		switch(opt)
		{
			case 1:{int y=rd(),z=rd();QA(x,y,z);break;}
			case 2:{int y=rd();printf("%d\n",QA(x,y,0));break;}
			case 3:{int z=rd();add(1,seg[x],seg[x]+sz[x]-1,z);break;}
			case 4:{printf("%d\n",wy(1,seg[x],seg[x]+sz[x]-1));break;}
		}
	}
	return 0;
}

题目大意:在一棵树上询问两个点之间的颜色段和修改一个节点的颜色。链接: https://loj.ac/problem/10141

思路:因为树链剖分查询时是从两个端点开始向上搜链,所以记录一下每次两条链连接的部分的颜色,相同就--;
线段树内记录左右儿子的左右端点的颜色,如果中间可合并为一段,就--;

 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll rd()
{
    ll x=0,ert=1;char lk=getchar();
    while(!isdigit(lk)){if(lk==-) ert=-1;lk=getchar();}
    while(isdigit(lk)){x=(x<<3)+(x<<1)+(lk-0);lk=getchar();}
    return x*ert;
}
const int N=1e5+10;
struct zx{int nx,to;}e[N<<1];
struct zy{int l,r,sum,fl,lc,rc;}s[N<<2];
int dep[N],cnt,fa[N],sz[N],son[N],id[N],a[N],A[N],top[N],h[N],nm,L,R;
void df(int x,int F)
{
    dep[x]=dep[fa[x]=F]+(sz[x]=1);
    for(int i=h[x],y;i;i=e[i].nx)
    {
        if((y=e[i].to)==F) continue;
        df(y,x);sz[x]+=sz[y];
        if(sz[y]>sz[son[x]]) son[x]=y;
    }
}
void dfs(int x,int F)
{
    a[id[x]=++cnt]=A[x];
    if(!son[x]) return ;
    top[son[x]]=top[x];dfs(son[x],x);
    for(int i=h[x],y;i;i=e[i].nx)
    {
        if((y=e[i].to)==F||y==son[x]) continue;
        dfs(top[y]=y,x);
    }
}
void zw(int p,int c){s[p].lc=c,s[p].rc=c,s[p].sum=1;s[p].fl=c;}
void up(int p)
{
    s[p].sum=s[p<<1].sum+s[p<<1|1].sum;
    s[p].lc=s[p<<1].lc;s[p].rc=s[p<<1|1].rc;
    if(s[p<<1].rc==s[p<<1|1].lc) s[p].sum--;
}
void biu(int p,int l,int r)
{
    s[p].l=l;s[p].r=r;
    if(l==r) {zw(p,a[l]);s[p].fl=0;return ;}
    biu(p<<1,l,(l+r)>>1);biu(p<<1|1,((l+r)>>1)+1,r);
    up(p);
    
}
void ud(int p){zw(p<<1,s[p].fl),zw(p<<1|1,s[p].fl),s[p].fl=0;}
void add(int p,int l,int r,int c)
{
    if(l<=s[p].l&&r>=s[p].r){zw(p,c);return ;}
    if(s[p].fl) ud(p);
    int mid=(s[p].l+s[p].r)>>1;
    if(l<=mid) add(p<<1,l,r,c);
    if(r>mid) add(p<<1|1,l,r,c);
    up(p);
}
int wy(int p,int l,int r)
{
    if(l<=s[p].l&&r>=s[p].r) {if(!L) L=s[p].lc;R=s[p].rc;return s[p].sum;}
    if(s[p].fl) ud(p);
    int mid=(s[p].l+s[p].r)>>1,x=0,y=0;
    if(l<=mid) x=wy(p<<1,l,r);
    if(r>mid) y=wy(p<<1|1,l,r);
    if(x&&y&&s[p<<1].rc==s[p<<1|1].lc) x--;
    up(p);
    return x+y;
}
int QA(int x,int y,int z)
{
    int ans=0,l[2]={0},fl=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y),fl^=1;
        if(z) add(1,id[top[x]],id[x],z);
        if(!z) {L=0;R=0;ans+=wy(1,id[top[x]],id[x]);if(l[fl]==R) ans--;l[fl]=L;}
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y),fl^=1;
    if(z) add(1,id[x],id[y],z);
    if(!z) {L=R=0;ans+=wy(1,id[x],id[y]);if(l[fl]==L) ans--;if(l[fl^1]==R) ans--;}
    return ans;
}
void Add(int x,int y){e[++nm].nx=h[x];e[nm].to=y;h[x]=nm;}
int main()
{
    int n=rd(),m=rd(),x,y,w;char c;
    for(int i=1;i<=n;i++) A[i]=rd();
    for(int i=1;i<n;i++) x=rd(),y=rd(),Add(x,y),Add(y,x);
    df(1,0);dfs(1,0);biu(1,1,n);
    for(int i=1;i<=m;i++)
    {
        cin>>c;
        x=rd(),y=rd();
        if(c==C) w=rd(),QA(x,y,w);
        else printf("%d\n",QA(x,y,0));
    }
    return 0;
}

题目大意:一棵树上每个节点有两个状态0/1,给出一个点 求此点到根节点的链上状态为1的节点数输出,并将这些节点状态赋0;或求以此节点为根节点的子数状态为0的个数输出,并将这些节点的状态赋1;

https://loj.ac/problem/2130

思路:一个区间内就两种状态,维护一下;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
void FRE(){freopen(".in","r",stdin);freopen(".out","w",stdout);}
void FCL(){fclose(stdin);fclose(stdout);}
inline ll rd()
{
	ll x=0,ert=1;char lk=getchar();
	while(!isdigit(lk)){if(lk==‘-‘) ert=-1;lk=getchar();}
	while(isdigit(lk)){x=(x<<3)+(x<<1)+(lk-‘0‘);lk=getchar();}
	return x*ert;
}
const int N=1e5+10;
struct zx{int nx,to;}e[N];
struct zy{int l,r,sum,fl;}s[N<<2];
int top[N],dep[N],cnt,son[N],fa[N],id[N],nm,h[N],sz[N];
void df(int x)
{
	dep[x]=dep[fa[x]]+(sz[x]=1);
	for(int i=h[x],y;i;i=e[i].nx)
	{
		df(y=e[i].to);sz[x]+=sz[y];
		if(sz[y]>sz[son[x]]) son[x]=y;
	}
}
void dfs(int x)
{
	id[x]=++cnt;
	if(!son[x]) return ;
	top[son[x]]=top[x];dfs(son[x]);
	for(int i=h[x],y;i;i=e[i].nx)
	{
		if((y=e[i].to)==son[x]) continue;
		top[y]=y;dfs(y);
	}
}
void biu(int p,int l,int r)
{
	s[p].l=l;s[p].r=r;s[p].fl=-1;s[p].sum=r-l+1;
	if(l==r) return ;
	biu(p<<1,l,(l+r)>>1);biu(p<<1|1,((l+r)>>1)+1,r);
}
void ud(int p,int x){s[p].sum=(s[p].r-s[p].l+1)*x;s[p].fl=x;}
int add(int p,int l,int r,int x,int y)
{
	if(l<=s[p].l&&r>=s[p].r)
	{
		int q=s[p].sum*x+(s[p].r-s[p].l+1-s[p].sum)*y;ud(p,y);
		return q;
	}
	if(s[p].fl>=0) ud(p<<1,s[p].fl),ud(p<<1|1,s[p].fl),s[p].fl=-1;
	int mid=(s[p].l+s[p].r)>>1,q=0;
	if(l<=mid) q+=add(p<<1,l,r,x,y);
	if(r>mid) q+=add(p<<1|1,l,r,x,y);
	s[p].sum=s[p<<1].sum+s[p<<1|1].sum;
	return q;
}
int AQ(int x)
{
	int ans=0;
	while(x)
	{
		ans+=add(1,id[top[x]],id[x],1,0);
		x=fa[top[x]];
	}
	return ans;
}
void add(int x,int y){e[++nm].to=y;e[nm].nx=h[x];h[x]=nm;fa[y]=x;}
int main()
{
	int n=rd();
	for(int i=1;i<n;i++) add(rd()+1,i+1);
	df(1);dfs(1);biu(1,1,n);
	int m=rd();
	char c[50];
	for(int i=1;i<=m;i++)
	{
		scanf("%s",c+1);int x=rd()+1;
		if(c[1]==‘i‘) printf("%d\n",AQ(x));
		else printf("%d\n",add(1,id[x],id[x]+sz[x]-1,0,1));
	}
	return 0;
}

 题目大意:给定一棵树,给每个节点一个种类,一个值,共n个节点,c种 种类,(1<=n,c<=1e5);

1:求两个节点之间(两个节点同类)与这两个节点同类的值和;

2:求两个节点之间(两个节点同类)与这两个节点同类的最大值;

3:修改某个节点的种类;

4:修改某个节点的值;

https://loj.ac/problem/2195

思路:建c颗线段树动态开点

#include<bits/stdc++.h>
using namespace std;
#define ll long long
void FRE(){freopen(".in","r",stdin);freopen(".out","w",stdout);}
void FCL(){fclose(stdin);fclose(stdout);}
inline ll rd()
{
    ll x=0,ert=1;char lk=getchar();
    while(!isdigit(lk)){if(lk==-) ert=-1;lk=getchar();}
    while(isdigit(lk)){x=(x<<3)+(x<<1)+(lk-0);lk=getchar();}
    return x*ert;
}
const int N=1e5+10;
struct zx{int nx,to;}e[N<<1];
int w[N],c[N],nm,n,h[N];
int ls[N<<4],rs[N<<4],sum[N<<4],mx[N<<4],rt[N],cnt;
int dep[N],fa[N],top[N],son[N],sz[N],id[N],cn;
void df(int x,int F)
{
    dep[x]=dep[fa[x]=F]+(sz[x]=1);
    for(int i=h[x],y;i;i=e[i].nx)
    {
        if((y=e[i].to)==F) continue;
        df(y,x);sz[x]+=sz[y];
        if(sz[y]>sz[son[x]]) son[x]=y;
    }
}
void dfs(int x)
{
    id[x]=++cn;
    if(!son[x]) return ;
    top[son[x]]=top[x];dfs(son[x]);
    for(int i=h[x],y;i;i=e[i].nx)
        if(!(dep[y=e[i].to]<dep[x]||y==son[x])) dfs(top[y]=y);
}
void up(int p){sum[p]=sum[ls[p]]+sum[rs[p]];mx[p]=max(mx[ls[p]],mx[rs[p]]);}
void cag(int &p,int l,int r,int su,int x)
{
    if(!p) p=++cnt;
    if(l==r){sum[p]=mx[p]=su;return ;}
    int mid=(l+r)>>1;
    if(x<=mid) cag(ls[p],l,(l+r)>>1,su,x);
    if(x>mid) cag(rs[p],((l+r)>>1)+1,r,su,x);
    up(p);
}
int askmx(int p,int l,int r,int x,int y)
{
    if(!p) return 0;
    if(x<=l&&y>=r) return mx[p];
    int mid=(l+r)>>1,ans=0;
    if(x<=mid) ans=askmx(ls[p],l,mid,x,y);
    if(y>mid) ans=max(ans,askmx(rs[p],mid+1,r,x,y));
    return ans;
}
int asksum(int p,int l,int r,int x,int y)
{
    if(!p) return 0;
    if(x<=l&&y>=r) return sum[p];
    int mid=(l+r)>>1,ans=0;
    if(x<=mid) ans=asksum(ls[p],l,mid,x,y);
    if(y>mid) ans+=asksum(rs[p],mid+1,r,x,y);
    return ans;
}
int QA(int x,int y,bool fl)
{
    int P=rt[c[x]],ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        if(fl) ans+=asksum(P,1,n,id[top[x]],id[x]);
        if(!fl) ans=max(ans,askmx(P,1,n,id[top[x]],id[x]));
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(fl) ans+=asksum(P,1,n,id[x],id[y]);
    if(!fl) ans=max(ans,askmx(P,1,n,id[x],id[y]));
    return ans;
}
void add(int x,int y){e[++nm].nx=h[x];h[x]=nm;e[nm].to=y;}
int main()
{
    n=rd();int Q=rd(),x,y;char p[5];
    for(int i=1;i<=n;i++) w[i]=rd(),c[i]=rd();
    for(int i=1;i<n;i++){x=rd(),y=rd();add(x,y);add(y,x);}
    df(1,0);dfs(1);
    for(int i=1;i<=n;i++) cag(rt[c[i]],1,n,w[i],id[i]);
    for(int i=1;i<=Q;i++)
    {
        scanf("%s",p);x=rd(),y=rd();
        if(p[0]==C)
        {
            if(p[1]==C) cag(rt[c[x]],1,n,0,id[x]),c[x]=y,cag(rt[c[x]],1,n,w[x],id[x]);
            if(p[1]==W) cag(rt[c[x]],1,n,y,id[x]),w[x]=y;
        }
        if(p[0]==Q)
        {
            if(p[1]==S) printf("%d\n",QA(x,y,1));
            if(p[1]==M) printf("%d\n",QA(x,y,0));
        }
    }
    return 0;
}

 

树链剖分

标签:dig   个数   --   查询   cas   struct   stdout   fine   for   

原文地址:https://www.cnblogs.com/LWL--Figthing/p/9756460.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!