树链剖分学习笔记

暂时搞完一系列字符串算法之后,开始了树链剖分算法的学习。

树链剖分算法,说白了就是一种特殊的DFS序(通过把节点分为重节点和轻节点来保证线段树的时间复杂度)然后在线段树上乱搞。

题目分为点权和边权两类,点权好理解一些,边权可以转化为边在树中指向节点的权值。网上博客挺多的,不一一赘述了。上习题。

ZJOI 2008 BZOJ 1036 COGS 1688 树的统计Count

点权的模板题,code:

w#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
struct hp{
	int wson,fat,size,dep,top;
}tree[30001];
struct hq{
	int u,v;
}a[60000];
struct hr{
	int maxn,sum;
}seg[120000];
int plc[30001],f[30001],val[30001];
int point[30000],next[60000];
int n,e=1,totw=0,m,ans=-2100000000;
void add(int u,int v)
{
	e++; a[e].u=u; a[e].v=v; next[e]=point[u]; point[u]=e;
	e++; a[e].u=v; a[e].v=u; next[e]=point[v]; point[v]=e;
}
void build_tree(int last,int x,int depth)
{
	int i;
	tree[x].dep=depth;
	tree[x].size=1;
	tree[x].fat=last;
	tree[x].wson=0;
	for (i=point[x];i;i=next[i])
	  if (a[i].v!=last)
	    {
	  	  build_tree(x,a[i].v,depth+1);
	  	  tree[x].size+=tree[a[i].v].size;
	  	  if (tree[tree[x].wson].size<tree[a[i].v].size)
	  	    tree[x].wson=a[i].v;
	    }
}
void build_seg(int now,int tp)
{
	int i;
	tree[now].top=tp;
	plc[now]=++totw;
	f[totw]=now;
	if (tree[now].wson!=0)
	  build_seg(tree[now].wson,tp);
	for (i=point[now];i;i=next[i])
	  if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat)
	    build_seg(a[i].v,a[i].v);
}
void updata(int i)
{
	seg[i].maxn=max(seg[i<<1].maxn,seg[i<<1|1].maxn);
	seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
}
void build(int i,int l,int r)
{
	if (l==r)
	  {
	    seg[i].maxn=seg[i].sum=val[f[l]];
	    return;
	  }
	build(lch); build(rch);
	updata(i);
}
void insert(int i,int l,int r,int x,int a)
{
	if (l==r&&l==x)
	  {
	    seg[i].maxn=seg[i].sum=a;
	    return;
	  }
	if (x<=mid) insert(lch,x,a);
	else insert(rch,x,a);
	updata(i);
}
void query_seg(int i,int l,int r,int x,int y,int kind)
{
	if (x<=l&&y>=r)
	  {
	    if (kind==0) ans=max(ans,seg[i].maxn);
	    if (kind==1) ans=ans+seg[i].sum;
	    return;
	  }
	if (x<=mid) query_seg(lch,x,y,kind);
	if (y>mid) query_seg(rch,x,y,kind);
}
void query(int x,int y,int kind)
{
	int f1=tree[x].top,f2=tree[y].top;
	while (f1!=f2) 
	  {
	    if (tree[f1].dep<tree[f2].dep) {swap(x,y); swap(f1,f2);}
	    query_seg(1,1,n,plc[f1],plc[x],kind);
	    x=tree[f1].fat;
	    f1=tree[x].top;
      }
    if (tree[x].dep>tree[y].dep)
	  swap(x,y);
	query_seg(1,1,n,plc[x],plc[y],kind);
	printf("%d\n",ans);  
}
int main()
{
	int i,x,y;
	char opt[10];
	scanf("%d",&n);
	for (i=1;i<=n-1;++i)
	  {
	    scanf("%d%d",&x,&y);
	    add(x,y);
	  }
	build_tree(0,1,0);
	build_seg(1,1);
	for (i=1;i<=n;++i)
	  scanf("%d",&val[i]);
	build(1,1,n);
	scanf("%d",&m);
	for (i=1;i<=m;++i)
	  {
	    scanf("%s",&opt);
	    if (opt[0]=='Q')
	      {
	      	scanf("%d%d",&x,&y); 
	        if (opt[1]=='M')
	          {ans=-2100000000;query(x,y,0);}
	        if (opt[1]=='S')
	          {ans=0;query(x,y,1);}
	      }
	    if (opt[0]=='C')
	      {
	        scanf("%d%d",&x,&y);
	        insert(1,1,n,plc[x],y);
	      }
	  }
}
COGS 1672 SPOJ QTREE

蛮有名的边权树链剖分,code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
struct hp{
	int size,fat,top,wson,dep;
}tree[10001];
struct hr{
	int x,y,w;
}qst[100001];
struct hq{
	int u,v;
}a[20001];
int point[10001],next[20001];
int plc[10001];
int seg[40001],val[10001];
int n,totw=0,e=1,ans=-2100000000;
char s[10];
void add(int u,int v)
{
	e++; a[e].u=u; a[e].v=v; next[e]=point[u]; point[u]=e;
	e++; a[e].u=v; a[e].v=u; next[e]=point[v]; point[v]=e;
}
void build_tree(int last,int x,int depth)
{
	int i;
	tree[x].size=1;
	tree[x].fat=last;
	tree[x].wson=0;
	tree[x].dep=depth;
	for (i=point[x];i;i=next[i])
	  if (a[i].v!=last)
	    {
	      build_tree(x,a[i].v,depth+1);
	      tree[x].size+=tree[a[i].v].size;
	      if (tree[tree[x].wson].size<tree[a[i].v].size)
	        tree[x].wson=a[i].v;
	    }
}
void build_seg(int now,int tp)
{
	int i;
	tree[now].top=tp;
	plc[now]=++totw;
	if (tree[now].wson!=0)
	  build_seg(tree[now].wson,tp);
	for (i=point[now];i;i=next[i])
	  if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat)
	    build_seg(a[i].v,a[i].v);
}
void updata(int i)
{
	seg[i]=max(seg[i<<1],seg[i<<1|1]);
}
void build(int i,int l,int r)
{
	if (l==r)
	  {
	    seg[i]=val[l];
	    return;
	  }
	build(lch); build(rch);
	updata(i);
}
void insert(int i,int l,int r,int x,int a)
{
	if (l==r&&l==x)
	  {
	    seg[i]=a;
	    return;
	  }
	if (x<=mid) insert(lch,x,a);
	else insert(rch,x,a);
	updata(i);
}
void query_seg(int i,int l,int r,int x,int y)
{
	if (x<=l&&y>=r)
	  {
	    ans=max(ans,seg[i]);
	    return;
	  }
	if (x<=mid) query_seg(lch,x,y);
	if (y>mid) query_seg(rch,x,y);
}
void query(int x,int y)
{
	int f1=tree[x].top,f2=tree[y].top;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);}
	    query_seg(1,1,n,plc[f1],plc[x]);
	    x=tree[f1].fat;
	    f1=tree[x].top;
	  }
	if (x==y) printf("%d\n",ans);
	else
	  {
	    if (tree[x].dep>tree[y].dep) swap(x,y);
	    query_seg(1,1,n,plc[tree[x].wson],plc[y]);
	    printf("%d\n",ans);
	  }
}
int main()
{
	int i,z,x,y;
	freopen("qtree.in","r",stdin);
	freopen("qtree.out","w",stdout);
	scanf("%d",&n);
	for (i=1;i<=n-1;++i)
	  {
	    scanf("%d%d%d",&qst[i].x,&qst[i].y,&qst[i].w);
	    add(qst[i].x,qst[i].y);
	  }
	build_tree(0,1,0);
	build_seg(1,1);
	for (i=1;i<=n;++i)
	  {
	    if (tree[qst[i].x].dep<tree[qst[i].y].dep)
	      swap(qst[i].x,qst[i].y);
	    val[plc[qst[i].x]]=qst[i].w;
	  }
	build(1,1,n);
	scanf("%s",&s);
	while (s[0]!='D')
	  {
	  	scanf("%d%d",&x,&y);
	    if (s[0]=='C')
		  insert(1,1,n,plc[qst[x].x],y);
		if (s[0]=='Q')
		  {
		    ans=-2100000000;
		    query(x,y);
	      }
	    scanf("%s",&s);
	  }
	fclose(stdin);
	fclose(stdout);
}
BZOJ 2157 COGS 1867 国家集训队2011 旅游

这题唯一不同于模板的地方在于线段树的操作,negate相当让maxn=-minn,minn=-maxn,然后这题没了code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
#define inf 2100000000
using namespace std;
struct hp{
	int size,top,fat,dep,wson;
}tree[100001];
struct hq{
	int x,y,w;
}qst[500001];
struct hr{
	int u,v;
}a[200001];
struct ht{
	int maxn,minn,f,sum;
}seg[400001];
int point[100001],next[200001];
int plc[100001],val[100001];
int n,ans,e=1,totw=0,m;
void add(int x,int y)
{
	e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e;
	e++; a[e].v=x; a[e].u=x; next[e]=point[y]; point[y]=e;
}
void build_tree(int last,int x,int depth)
{
	int i;
	tree[x].size=1;
	tree[x].fat=last;
	tree[x].wson=0;
	tree[x].dep=depth;
	for (i=point[x];i;i=next[i])
	  if (a[i].v!=last)
	    {
	      build_tree(x,a[i].v,depth+1);
	      tree[x].size+=tree[a[i].v].size;
	      if (tree[tree[x].wson].size<tree[a[i].v].size)
	        tree[x].wson=a[i].v;
	    }
}
void build_seg(int now,int tp)
{
	int i;
	tree[now].top=tp;
	plc[now]=++totw;
	if (tree[now].wson!=0)
	  build_seg(tree[now].wson,tp);
	for (i=point[now];i;i=next[i])
	  if (a[i].v!=tree[now].fat&&a[i].v!=tree[now].wson)
	    build_seg(a[i].v,a[i].v);
}
void updata(int i)
{
	seg[i].maxn=max(seg[i<<1].maxn,seg[i<<1|1].maxn);
	seg[i].minn=min(seg[i<<1].minn,seg[i<<1|1].minn);
	seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
}
void paint(int i)
{
	int x=seg[i].minn,y=seg[i].maxn;
	seg[i].maxn=-x; seg[i].minn=-y;
	seg[i].sum=-seg[i].sum;
	seg[i].f++;
}
void pushdown(int i)
{
	paint(i<<1); paint(i<<1|1);
	seg[i].f=0;
}
void build(int i,int l,int r)
{
	if (l==r)
	  {
	    seg[i].maxn=seg[i].sum=seg[i].minn=val[l];
	    return;
	  }
	build(lch); build(rch);
	updata(i);
}
void insert(int i,int l,int r,int x,int a)
{
	if (l==r&&l==x)
	  {
	    seg[i].maxn=seg[i].minn=seg[i].sum=a;
	    return;
	  }
	if (seg[i].f%2)
	  pushdown(i);
	if (x<=mid) insert(lch,x,a);
	else insert(rch,x,a);
	updata(i);
}
void query(int i,int l,int r,int x,int y,int kind)
{
	if (x<=l&&y>=r)
	  {
	    if (kind==1) ans=ans+seg[i].sum;
	    if (kind==2) ans=max(ans,seg[i].maxn);
	    if (kind==3) ans=min(ans,seg[i].minn);
	    return;
	  }
	if (seg[i].f%2)
	  pushdown(i);
	if (x<=mid) query(lch,x,y,kind);
	if (y>mid) query(rch,x,y,kind);
}
void opst(int i,int l,int r,int x,int y)
{
	if (x<=l&&y>=r)
	  {
	    paint(i);
	    return;
	  }
	if (seg[i].f%2)
	  pushdown(i);
	if (x<=mid) opst(lch,x,y);
	if (y>mid) opst(rch,x,y);
	updata(i);  
}
void work(int x,int y,int kind)
{
	int f1=tree[x].top,f2=tree[y].top;
	if (kind==1) ans=0;
	if (kind==2) ans=-inf;
	if (kind==3) ans=inf;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep) {swap(f1,f2);swap(x,y);}
	    if (kind==0) opst(1,1,n,plc[f1],plc[x]);
	    else  query(1,1,n,plc[f1],plc[x],kind);
	    x=tree[f1].fat; f1=tree[x].top;
	  }
	if (x!=y)
	  {
	    if (tree[x].dep>tree[y].dep) swap(x,y);
	    if (kind==0) opst(1,1,n,plc[tree[x].wson],plc[y]);
	    else  query(1,1,n,plc[tree[x].wson],plc[y],kind);
      }
    if (kind>0)
      printf("%d\n",ans);
}
int main()
{
	int i,x,y;
	char s[10];
	scanf("%d",&n);
	for (i=1;i<=n-1;++i)
	  {
	    scanf("%d%d%d",&qst[i].x,&qst[i].y,&qst[i].w);
	    qst[i].x++;
	    qst[i].y++;
	    add(qst[i].x,qst[i].y);
	  }
	build_tree(0,1,0);
	build_seg(1,1);
	for (i=1;i<=n-1;++i)
	  {
	    if (tree[qst[i].x].dep<tree[qst[i].y].dep) swap(qst[i].x,qst[i].y);
	    val[plc[qst[i].x]]=qst[i].w;
	  }
	build(1,1,n);
	scanf("%d",&m);
	for (i=1;i<=m;++i)
	  {
	    scanf("%*c%s%d%d",&s,&x,&y);
	    if (s[0]!='C') {x++; y++;}
	    if (s[0]=='N')
	      work(x,y,0);
		if (s[0]=='C')
		  insert(1,1,n,plc[qst[x].x],y);
		if (s[0]=='S')
		  work(x,y,1);
		if (s[0]=='M')
		  {
		    if (s[1]=='A')
		      work(x,y,2);
		    if (s[1]=='I')
		      work(x,y,3);
		  }  
	  }
}
HAOI 2015 BZOJ 4034 COGS 1963 树上操作

考虑链剖其实是一种特殊的DFS序,那么一个点的子树必定在线段树里是连续的,在build_seg里更新一下最左最右位置即可。code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
#define inf 2100000000
using namespace std;
struct hp{
	int size,wson,top,fat,dep,l,r;
}tree[100001];
struct hq{
	int u,v;
}a[200001];
int point[100001],next[200001];
int val[100001],plc[100001],f[100001];
long long seg[400001],delta[400001];
int n,m,totw=0,e=1;
long long ans;
void add(int x,int y)
{
	e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e;
	e++; a[e].v=x; a[e].u=y; next[e]=point[y]; point[y]=e;
}
void build_tree(int last,int x,int depth)
{
	int i;
	tree[x].size=1;
	tree[x].wson=0;
	tree[x].fat=last;
	tree[x].dep=depth;
	for (i=point[x];i;i=next[i])
	  if (a[i].v!=last)
	    {
	      build_tree(x,a[i].v,depth+1);
	      tree[x].size+=tree[a[i].v].size;
	      if (tree[tree[x].wson].size<tree[a[i].v].size)
	        tree[x].wson=a[i].v;
	    }
}
void build_seg(int now,int tp)
{
	int i;
	tree[now].top=tp;
	tree[now].l=plc[now]=++totw;
	f[totw]=now;
	if (tree[now].wson!=0)
	  build_seg(tree[now].wson,tp);
	for (i=point[now];i;i=next[i])
	  if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat)
	    build_seg(a[i].v,a[i].v);
	tree[now].r=totw;
}
void updata(int i)
{
	seg[i]=seg[i<<1]+seg[i<<1|1];
}
void build(int i,int l,int r)
{
	if (l==r)
	  {
	    seg[i]=val[f[l]];
		return;   
	  }
	build(lch); build(rch);
	updata(i);
}
void paint(int i,int l,int r,long long a)
{
	seg[i]=(long long)((long long)((long long)(r-l+1)*(long long)(a))+seg[i]);
	delta[i]=(long long)((long long)(a)+(long long)(delta[i]));
}
void pushdown(int i,int l,int r)
{
	paint(lch,delta[i]);
	paint(rch,delta[i]);
	delta[i]=0;
}
void query_seg(int i,int l,int r,int x,int y)
{
	if (x<=l&&y>=r)
	  {
	    ans=(long long)ans+(long long)seg[i];
	    return;
	  }
	if (delta[i]!=0)
	  pushdown(i,l,r);
	if (x<=mid) query_seg(lch,x,y);
	if (y>mid) query_seg(rch,x,y);
}
void insert(int i,int l,int r,int x,int y,int a)
{
	if (x<=l&&y>=r)
	  {
	    paint(i,l,r,a);
	    return;
	  }
	if (delta[i]!=0)
	  pushdown(i,l,r);
	if (x<=mid) insert(lch,x,y,a);
	if (y>mid) insert(rch,x,y,a);
	updata(i);
}
void query(int x,int y)
{
	int f1=tree[x].top,f2=tree[y].top;
	ans=0;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);}
	    query_seg(1,1,n,plc[f1],plc[x]);
	    x=tree[f1].fat; f1=tree[x].top;
	  }
	if (tree[x].dep>tree[y].dep)
	  swap(x,y);
	query_seg(1,1,n,plc[x],plc[y]);
	printf("%lld\n",ans);
}
int main()
{
	int i,x,y,opt;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;++i)
	  scanf("%d",&val[i]);
	for (i=1;i<=n-1;++i)
	  {
	    scanf("%d%d",&x,&y);
	    add(x,y);
	  }
	build_tree(0,1,0);
	build_seg(1,1);
	build(1,1,n);
	for (i=1;i<=m;++i)
	  {
	    scanf("%d",&opt);
	    if (opt==1)
	      {
	        scanf("%d%d",&x,&y);
	        insert(1,1,n,plc[x],plc[x],y);
	      }
	    if (opt==2)
	      {
	        scanf("%d%d",&x,&y);
	        insert(1,1,n,tree[x].l,tree[x].r,y);
	      }
	    if (opt==3)
	      {
	        scanf("%d",&x);
	        query(1,x);
	      }
	  }
}
SDOI 2011 BZOJ 2243 染色

链剖与前几道题是一样的,唯一多出来的就是线段树左右端点颜色的分类讨论,(PS:逗比把rightc写成leftc插了一个多小时= =)code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
struct hp{
	int size,fat,dep,wson,top;
}tree[100001];
struct hq{
	int u,v;
}a[200001];
struct hr{
	int lc,rc,sum,delta;
}seg[400001];
int point[100001],next[200001];
int val[100001],f[100001],plc[100001];
int n,m,totw=0,ans,e=1,leftc,rightc;
void add(int x,int y)
{
	e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e;
	e++; a[e].u=y; a[e].v=x; next[e]=point[y]; point[y]=e;
}
void build_tree(int last,int x,int depth)
{
	int i;
	tree[x].size=1;
	tree[x].dep=depth;
	tree[x].fat=last;
	tree[x].wson=0;
	for (i=point[x];i;i=next[i])
	  if (a[i].v!=last)
	    {
	      build_tree(x,a[i].v,depth+1);
		  tree[x].size+=tree[a[i].v].size;
		  if (tree[tree[x].wson].size<tree[a[i].v].size)
		    tree[x].wson=a[i].v;
	    }
}
void build_seg(int now,int tp)
{
	int i;
	tree[now].top=tp;
	plc[now]=++totw; f[totw]=now;
	if (tree[now].wson!=0)
	  build_seg(tree[now].wson,tp);
	for (i=point[now];i;i=next[i])
	  if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat)
	    build_seg(a[i].v,a[i].v);
}
void updata(int i)
{
	seg[i].lc=seg[i<<1].lc; seg[i].rc=seg[i<<1|1].rc;
	if (seg[i<<1].rc!=seg[i<<1|1].lc)
	  seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
	else
	  seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum-1;
}
void paint(int i,int a)
{
	seg[i].lc=seg[i].rc=a; seg[i].sum=1;
	seg[i].delta=a;
}
void pushdown(int i)
{
	paint(i<<1,seg[i].delta);
	paint(i<<1|1,seg[i].delta);
	seg[i].delta=-1;
}
void build(int i,int l,int r)
{
	seg[i].delta=-1;
	if (l==r)
	  {
	    seg[i].lc=seg[i].rc=val[f[l]];
		seg[i].sum=1;
		return; 
	  }
	build(lch); build(rch);
	updata(i);
}
void insert_seg(int i,int l,int r,int x,int y,int a)
{
	if (x<=l&&y>=r)
	  {
	    paint(i,a);
	    return;
	  }
	if (seg[i].delta!=-1)
	  pushdown(i);
	if (x<=mid) insert_seg(lch,x,y,a);
	if (y>mid) insert_seg(rch,x,y,a);
	updata(i);
}
void insert(int x,int y,int z)
{
	int f1=tree[x].top,f2=tree[y].top;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);}
	    insert_seg(1,1,n,plc[f1],plc[x],z);
	    x=tree[f1].fat; f1=tree[x].top;
	  }
	if (tree[x].dep>tree[y].dep) swap(x,y);
	insert_seg(1,1,n,plc[x],plc[y],z);
}
void query_seg(int i,int l,int r,int x,int y)
{
	int lc,rc;
	if (l==x) leftc=seg[i].lc; if (r==y) rightc=seg[i].rc;
	if (x<=l&&y>=r)
	  {
	    ans+=seg[i].sum;
	    return;
	  }
	if (seg[i].delta!=-1)
	  pushdown(i);
	lc=-1; rc=-1;
	if (x<=mid) {query_seg(lch,x,y);lc=seg[i<<1].rc;}
	if (y>mid) {query_seg(rch,x,y);rc=seg[i<<1|1].lc;}
	if (lc==rc&&lc!=-1&&rc!=-1) ans--;
}
void query(int x,int y)
{
	int f1=tree[x].top,f2=tree[y].top,tot=0;
	int lcr=-1,rcr=-1; ans=0;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep)
	      {
	        query_seg(1,1,n,plc[f2],plc[y]);
	        if (rcr==rightc) ans--; rcr=leftc;
	        y=tree[f2].fat; f2=tree[y].top;
	      }
	    else
	      {
	        query_seg(1,1,n,plc[f1],plc[x]);
	        if (lcr==rightc) ans--; lcr=leftc;
	        x=tree[f1].fat; f1=tree[x].top;
	      }
	  }
	if (tree[x].dep<tree[y].dep)
	  {
	    query_seg(1,1,n,plc[x],plc[y]);
	    if (rcr==rightc) ans--;
	    if (lcr==leftc) ans--;
	  }
	else
	  {
	    query_seg(1,1,n,plc[y],plc[x]);
	    if (rcr==leftc) ans--;
	    if (lcr==rightc) ans--;
	  }
	printf("%d\n",ans);
}
int main()
{
	int i,x,y,z;
	char c;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;++i)
	  scanf("%d",&val[i]);
	for (i=1;i<=n-1;++i)
	  {
	    scanf("%d%d",&x,&y);
	    add(x,y);
	  }
	build_tree(0,1,0);
	build_seg(1,1);
	build(1,1,n);
	for (i=1;i<=m;++i)
	  {
	    scanf("%c",&c);
	    while (c!='C'&&c!='Q')
	      scanf("%c",&c);
	    if (c=='C')
	      {
	        scanf("%d%d%d",&x,&y,&z);
	        insert(x,y,z);
	      }
	    if (c=='Q')
	      {
	        scanf("%d%d",&x,&y);
	        query(x,y);
	      }
	  }
}
SDOI 2014 BZOJ 3531 旅行

这题自己一开始的想法是按每种宗教建一棵线段树,内存炸的飞起,看完黄学长的博客才焕然大悟,其实只需要对每种宗教建一棵局部线段树即可。code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
struct hp{
	int size,fat,dep,wson,top;
}tree[100001];
struct hq{
	int u,v;
}a[200001];
struct hr{
	int lc,rc,sum,delta;
}seg[400001];
int point[100001],next[200001];
int val[100001],f[100001],plc[100001];
int n,m,totw=0,ans,e=1,leftc,rightc;
void add(int x,int y)
{
	e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e;
	e++; a[e].u=y; a[e].v=x; next[e]=point[y]; point[y]=e;
}
void build_tree(int last,int x,int depth)
{
	int i;
	tree[x].size=1;
	tree[x].dep=depth;
	tree[x].fat=last;
	tree[x].wson=0;
	for (i=point[x];i;i=next[i])
	  if (a[i].v!=last)
	    {
	      build_tree(x,a[i].v,depth+1);
		  tree[x].size+=tree[a[i].v].size;
		  if (tree[tree[x].wson].size<tree[a[i].v].size)
		    tree[x].wson=a[i].v;
	    }
}
void build_seg(int now,int tp)
{
	int i;
	tree[now].top=tp;
	plc[now]=++totw; f[totw]=now;
	if (tree[now].wson!=0)
	  build_seg(tree[now].wson,tp);
	for (i=point[now];i;i=next[i])
	  if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat)
	    build_seg(a[i].v,a[i].v);
}
void updata(int i)
{
	seg[i].lc=seg[i<<1].lc; seg[i].rc=seg[i<<1|1].rc;
	if (seg[i<<1].rc!=seg[i<<1|1].lc)
	  seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
	else
	  seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum-1;
}
void paint(int i,int a)
{
	seg[i].lc=seg[i].rc=a; seg[i].sum=1;
	seg[i].delta=a;
}
void pushdown(int i)
{
	paint(i<<1,seg[i].delta);
	paint(i<<1|1,seg[i].delta);
	seg[i].delta=-1;
}
void build(int i,int l,int r)
{
	seg[i].delta=-1;
	if (l==r)
	  {
	    seg[i].lc=seg[i].rc=val[f[l]];
		seg[i].sum=1;
		return; 
	  }
	build(lch); build(rch);
	updata(i);
}
void insert_seg(int i,int l,int r,int x,int y,int a)
{
	if (x<=l&&y>=r)
	  {
	    paint(i,a);
	    return;
	  }
	if (seg[i].delta!=-1)
	  pushdown(i);
	if (x<=mid) insert_seg(lch,x,y,a);
	if (y>mid) insert_seg(rch,x,y,a);
	updata(i);
}
void insert(int x,int y,int z)
{
	int f1=tree[x].top,f2=tree[y].top;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);}
	    insert_seg(1,1,n,plc[f1],plc[x],z);
	    x=tree[f1].fat; f1=tree[x].top;
	  }
	if (tree[x].dep>tree[y].dep) swap(x,y);
	insert_seg(1,1,n,plc[x],plc[y],z);
}
void query_seg(int i,int l,int r,int x,int y)
{
	int lc,rc;
	if (l==x) leftc=seg[i].lc; if (r==y) rightc=seg[i].rc;
	if (x<=l&&y>=r)
	  {
	    ans+=seg[i].sum;
	    return;
	  }
	if (seg[i].delta!=-1)
	  pushdown(i);
	lc=-1; rc=-1;
	if (x<=mid) {query_seg(lch,x,y);lc=seg[i<<1].rc;}
	if (y>mid) {query_seg(rch,x,y);rc=seg[i<<1|1].lc;}
	if (lc==rc&&lc!=-1&&rc!=-1) ans--;
}
void query(int x,int y)
{
	int f1=tree[x].top,f2=tree[y].top,tot=0;
	int lcr=-1,rcr=-1; ans=0;
	while (f1!=f2)
	  {
	    if (tree[f1].dep<tree[f2].dep)
	      {
	        query_seg(1,1,n,plc[f2],plc[y]);
	        if (rcr==rightc) ans--; rcr=leftc;
	        y=tree[f2].fat; f2=tree[y].top;
	      }
	    else
	      {
	        query_seg(1,1,n,plc[f1],plc[x]);
	        if (lcr==rightc) ans--; lcr=leftc;
	        x=tree[f1].fat; f1=tree[x].top;
	      }
	  }
	if (tree[x].dep<tree[y].dep)
	  {
	    query_seg(1,1,n,plc[x],plc[y]);
	    if (rcr==rightc) ans--;
	    if (lcr==leftc) ans--;
	  }
	else
	  {
	    query_seg(1,1,n,plc[y],plc[x]);
	    if (rcr==leftc) ans--;
	    if (lcr==rightc) ans--;
	  }
	printf("%d\n",ans);
}
int main()
{
	int i,x,y,z;
	char c;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;++i)
	  scanf("%d",&val[i]);
	for (i=1;i<=n-1;++i)
	  {
	    scanf("%d%d",&x,&y);
	    add(x,y);
	  }
	build_tree(0,1,0);
	build_seg(1,1);
	build(1,1,n);
	for (i=1;i<=m;++i)
	  {
	    scanf("%c",&c);
	    while (c!='C'&&c!='Q')
	      scanf("%c",&c);
	    if (c=='C')
	      {
	        scanf("%d%d%d",&x,&y,&z);
	        insert(x,y,z);
	      }
	    if (c=='Q')
	      {
	        scanf("%d%d",&x,&y);
	        query(x,y);
	      }
	  }
}

你可能感兴趣的:(树链剖分学习笔记)