[SDOI2014] 旅行

传送门

拿到这题,我:树链剖分+线段树!

哎呀C有点大。

动态开点!

不会RE吗…

其实是可能会RE的,为什么呢,我们想,最坏情况是每个点有不同的信仰,那么这个时候我们利用的空间是 n log ⁡ n n\log n nlogn n n n最坏情况下取 1 0 5 10^5 105时,占用空间是 13.2 × 1 0 5 13.2\times10^5 13.2×105左右,加上修改需要两倍的空间那么应该保险起见线段树需要开27倍左右,但是我看题解里面好像都是开的20倍,有哪位大佬能给我解释一下吗qwq。

开27倍空间也许会MLE,为了保险起见,我用了一个还挺好写的一个优化,就是开一个队列,队列里面存的是之前已经删掉了那些点,当我们要新加入一个点的时候,首先看这个队列里面有没有值,如果有就往那个位置上存,否则就++tot。

大概写出来是这个样子滴:

queue<int> trashcan;

int _new(){
	if(trashcan.empty())return ++tot;
	int x=trashcan.front();trashcan.pop();
	return x;
}

void clear(int u){
	seg[u].lc=seg[u].rc=0;
	seg[u].sum=seg[u]._max=0;
	trashcan.push(u);	
}

然后就正常的动态开点维护就可以了

这样开20倍空间就没有问题啦

# include 
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=1e5+5;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int n,m;
int head[N],cnt;
int belief[N],lv[N];
int faz[N],son[N],dep[N],siz[N],top[N],dfn[N],dfx;
int root[N],tot;
queue<int> trashcan;

struct Edge{
	int to,next;
}e[N<<1];

void add(int x,int y){
	e[++cnt]=(Edge){y,head[x]},head[x]=cnt;	
}

struct segment_tree{
	int lc,rc;
	int sum,_max;	
}seg[N*20];

void pushup(int u){
	seg[u].sum=seg[seg[u].lc].sum+seg[seg[u].rc].sum;
	seg[u]._max=max(seg[seg[u].lc]._max,seg[seg[u].rc]._max);	
}

int _new(){
	if(trashcan.empty())return ++tot;
	int x=trashcan.front();trashcan.pop();
	return x;
}

void clear(int u){
	seg[u].lc=seg[u].rc=0;
	seg[u].sum=seg[u]._max=0;
	trashcan.push(u);	
}

int insert(int u,int l,int r,int x,int k){
	if(!u)u=_new();
	if(l==r){
		seg[u].sum=seg[u]._max=k;
		return u;
	}
	int mid=l+r>>1;
	if(x<=mid)seg[u].lc=insert(seg[u].lc,l,mid,x,k);
	else seg[u].rc=insert(seg[u].rc,mid+1,r,x,k);
	pushup(u);
	return u;
}

int erase(int u,int l,int r,int x){
	if(l==r){
		clear(u);
		return 0;
	}
	int mid=l+r>>1;
	if(x<=mid)seg[u].lc=erase(seg[u].lc,l,mid,x);
	else seg[u].rc=erase(seg[u].rc,mid+1,r,x);
	pushup(u);
	if(!seg[u].lc&&!seg[u].rc){
		clear(u);
		u=0;	
	}
	return u;
}

int Getsum(int u,int l,int r,int ql,int qr){
	if(l>=ql&&r<=qr)return seg[u].sum;
	int mid=l+r>>1;
	int res=0;
	if(ql<=mid&&seg[u].lc)res+=Getsum(seg[u].lc,l,mid,ql,qr);
	if(qr>mid&&seg[u].rc)res+=Getsum(seg[u].rc,mid+1,r,ql,qr);
	return res;	
}

int Getmax(int u,int l,int r,int ql,int qr){
	if(l>=ql&&r<=qr)return seg[u]._max;
	int mid=l+r>>1;
	int res=0;
	if(ql<=mid&&seg[u].lc)res=max(res,Getmax(seg[u].lc,l,mid,ql,qr));
	if(qr>mid&&seg[u].rc)res=max(res,Getmax(seg[u].rc,mid+1,r,ql,qr));
	return res;	
}

void BeliefModify(int x,int y){
	root[belief[x]]=erase(root[belief[x]],1,n,dfn[x]);
	belief[x]=y;
	root[belief[x]]=insert(root[belief[x]],1,n,dfn[x],lv[x]);
}

void LevelModify(int x,int y){
	lv[x]=y;
	root[belief[x]]=insert(root[belief[x]],1,n,dfn[x],lv[x]);	
}

void RouteQuerySum(int x,int y){
	int res=0,be=belief[y];
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res+=Getsum(root[be],1,n,dfn[top[x]],dfn[x]);
		x=faz[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	res+=Getsum(root[be],1,n,dfn[x],dfn[y]);
	printf("%d\n",res);
}

void RouteQueryMax(int x,int y){
	int res=0,be=belief[y];
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res=max(res,Getmax(root[be],1,n,dfn[top[x]],dfn[x]));
		x=faz[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	res=max(res,Getmax(root[be],1,n,dfn[x],dfn[y]));
	printf("%d\n",res);
}

void dfs1(int u,int fa){
	faz[u]=fa;
	siz[u]=1;
	dep[u]=dep[fa]+1;
	RepG(i,u){
		int v=e[i].to;
		if(v==fa)continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

void dfs2(int u,int _top){
	top[u]=_top;
	dfn[u]=++dfx;
	if(!son[u])return;
	dfs2(son[u],_top);
	RepG(i,u){
		int v=e[i].to;
		if(v==faz[u]||v==son[u])continue;
		dfs2(v,v);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	read(n),read(m);
	Rep(i,1,n)read(lv[i]),read(belief[i]);
	Rep(i,1,n-1){
		int x,y;
		read(x),read(y);
		add(x,y),add(y,x);
	}
	dfs1(1,0),dfs2(1,1);
	Rep(i,1,n)root[belief[i]]=insert(root[belief[i]],1,n,dfn[i],lv[i]);
	Rep(i,1,m){
		char opt[10];
		int x,y;
		scanf("%s%d%d",opt,&x,&y);
		if(opt[0]=='C')
			if(opt[1]=='C')BeliefModify(x,y);
			else LevelModify(x,y);
		else 
			if(opt[1]=='S')RouteQuerySum(x,y);
			else RouteQueryMax(x,y);
	}
	return 0;
}

你可能感兴趣的:(#,树链剖分,#,线段树)