• 树链剖分


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

    模板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
    ",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
    ",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
    ",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
    ",AQ(x));
    		else printf("%d
    ",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
    ",QA(x,y,1));
                if(p[1]=='M') printf("%d
    ",QA(x,y,0));
            }
        }
        return 0;
    }
  • 相关阅读:
    集训笔记——dp继续
    集训笔记——各种dp(dp杂谈)
    集训笔记——dp
    洛谷P3197 [HNOI2008]越狱 题解
    集训笔记——杂题选讲(图论,dp)
    集训笔记——杂题选讲(带数学推导的递推、递归和dp,卡特兰数)
    滑动窗口+二分--P3957 跳房子
    差分+二分答案--P1083 借教室
    逆序对--P1966 火柴排队
    数位dp--P2657 [SCOI2009] windy 数
  • 原文地址:https://www.cnblogs.com/LWL--Figthing/p/9756460.html
Copyright © 2020-2023  润新知