雨天的尾巴(树上差分+线段树合并)

哇这道题 恶心死我
首先要知道,树上差分一般解决的问题是关于树上的覆盖问题 然后遇到覆盖问题尽量不要打树剖(会慢很多)
关于此题 因为这道题覆盖的是 从(x)(y)的点 所以我们在 (x,y)上打(kind) (1)的标记 然后在(lca(x,y),fa(lca(x,y)))上打(kind) (-1)的标记因为lca会被覆盖两次所以要打一个(-1)标记
那么这道题 我们可以对于每个(kind)都开一颗线段树啊。(理想很美好) 然而会爆空间
这个时候就需要考虑动态开点+线段树合并+垃圾回收
动态开点 就是如果要用当前点并且这个点的信息没有 那么就可以开这个点
垃圾回收 如果当前点不用了 就把他删了,把他的信息清零 然后把他放入栈中表示 当前点为空
线段树合并 将两颗线段树合并在一起,即传递了信息,也节省了空间 一举两得
然后这道题 相当于(但实际处理的时候用过的线段树就把它删了)对于每个节点 都开一颗线段树 存储区间(l)~(r)的信息 (l)~(r)表示的是这些一段区间 种类(kind)的最大值以及其(cf)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=110000;
const int maxm=2000000;
const int M=1000000000;
int fa[maxn][30],first[maxn],next[maxn<<2],to[maxn<<2];
int val[maxn<<2],kind[maxn<<2],cf[maxm],kcf[maxm];
int root[maxm],ans[maxn];
int dep[maxn];
int size[maxn],rt_size[maxn];
int n,m,tot;
int rs[maxm],ls[maxm];
int rub[maxm],top,cnt=0;
queue<int> q;
int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9')
		ch=getchar();
	while(ch>='0' && ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}

void add(int x,int y)
{
	tot++;
	next[tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}

void bfs()//这里只能用bfs 用dfs要爆栈
{
	int qq[maxn],head,tail;
	head=1;tail=0;
	qq[++tail]=1;
	while(head<=tail)
	{
		int x=qq[head];head++;
		size[x]=1;dep[x]=dep[fa[x][0]]+1;
		for(int i=0;i<=20;i++)
			fa[x][i+1]=fa[fa[x][i]][i];
		for(int i=first[x];i;i=next[i])
		{
			int y=to[i];
			if(y==fa[x][0])	continue;
			fa[y][0]=x;
			qq[++tail]=y;
		}
	}
	while(tail)//利用bfs的性质 将非叶节点 更新 然后将叶节点 入队 
	{
		size[fa[qq[tail]][0]]+=size[qq[tail]];
		if(size[qq[tail]]==1)
			q.push(qq[tail]);
		tail--;
	}
}

int LCA(int x,int y)//LCA
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=21;i>=0;i--)
	{	
		if(dep[fa[x][i]]>=dep[y])	x=fa[x][i];
		if(y==x)	return x;
	}
	for(int i=21;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}

void insert(int x,int k,int wow)//相当于将x与其信息连边
{
	tot++;
	next[tot]=first[x];
	first[x]=tot;
	val[tot]=wow;
	kind[tot]=k;
}

int newnode()
{
	if(top)	return rub[top--];
	return ++cnt;
}

void push_up(int node)//用儿子节点更新当前节点
{
	if(cf[ls[node]]>=cf[rs[node]])//因为要求当前节点的覆盖尽量大的kind
	{
		cf[node]=cf[ls[node]];
		kcf[node]=kcf[ls[node]];
	}
	else
	{
		cf[node]=cf[rs[node]];
		kcf[node]=kcf[rs[node]];
	}
}

void update(int l,int r,int &node,int k,int pri)
{
	if(!node)	node=newnode();//如果当前节点为空 新建一个节点
	if(l==r){
		cf[node]+=pri;//增加当前 pri 值
		kcf[node]=l;// 将当前节点kind 赋值
	}
	else{
		int mid=(l+r)>>1;
		if(k<=mid){
			update(l,mid,ls[node],k,pri);	
		}
		else{
			update(mid+1,r,rs[node],k,pri);
		}
		push_up(node);//一定要打push_up
	}
	if(!cf[node])//如果当前差分值为0 那么说明当前节点不被任何kind覆盖
		kcf[node]=0;//那么当前节点的kind=0
}

void del(int x)//删除
{
	rub[++top]=x;//将当前点加入栈中
	ls[x]=0;//将当前点信息清零
	rs[x]=0;
	cf[x]=0;
}

int merge(int l,int r,int nodeone,int nodetwo)
{
	if(!nodeone || !nodetwo)	return nodeone+nodetwo;//线段树合并如果对应点为某一个为0 返回另一个点的node
	int node=newnode();
	if(l==r)
	{
		cf[node]=cf[nodeone]+cf[nodetwo];//更新合并节点cf值 因为cf值要从子节点更新上来
		kcf[node]=l;//合并节点的 kind 因为当前合并点区间l==r 所以可以更新
	}
	else
	{
		int mid=(l+r)>>1;
		ls[node]=merge(l,mid,ls[nodeone],ls[nodetwo]);
		rs[node]=merge(mid+1,r,rs[nodeone],rs[nodetwo]);
		push_up(node);//此处应该push_up    fuck!!!!!
	}
	del(nodeone);del(nodetwo);//删除 因为这两个节点已经被新的节点代替
	return node;
}

int main()
{
	n=read();m=read();
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		x=read();y=read();
		add(x,y);add(y,x);
	}
	bfs();
	memset(first,0,sizeof(first));
	tot=0;
	for(int i=1;i<=m;i++)
	{
		int x,y,z,lca;
		x=read();y=read();z=read();
		lca=LCA(x,y);
		insert(x,z,1);//这里的insert表示的是 存储与x有关的信息 
		insert(y,z,1);
		insert(lca,z,-1);
		insert(fa[lca][0],z,-1);
	}
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int i=first[x];i;i=next[i])//遍历与x有关的信息
			update(1,M,root[x],kind[i],val[i]);//更新线段树信息 
		ans[x]=kcf[root[x]];//相当于x 对应的线段树的根节点
		root[fa[x][0]]=merge(1,M,root[x],root[fa[x][0]]);//合并他和他的父亲 即更新了信息 又节省了空间
		rt_size[fa[x][0]]+=size[x];//这两行相当于一个判重 每个点只会入队一次
		if(rt_size[fa[x][0]]+1==size[fa[x][0]])
			q.push(fa[x][0]);
	}
	for(int i=1;i<=n;i++)
		printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/mendessy/p/11716245.html