zoukankan      html  css  js  c++  java
  • 雨天的尾巴(树上差分+线段树合并)

    哇这道题 恶心死我
    首先要知道,树上差分一般解决的问题是关于树上的覆盖问题 然后遇到覆盖问题尽量不要打树剖(会慢很多)
    关于此题 因为这道题覆盖的是 从(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;
    }
    
  • 相关阅读:
    Linux 打印可变参数日志
    am335x system upgrade kernel f-ram fm25l16b(十六)
    spi signal analyze using logic analyzer
    learning armbian steps(3) ----- armbian 文件系统定制思路
    learning armbian steps(2) ----- armbian 镜像编译
    learning armbian steps(1) ----- armbian 入门知识基础学习
    create rootfs.img using loop device
    learning docker steps(8) ----- docker network 初次体验
    learning docker steps(7) ----- docker registry 搭建
    learning docker steps(6) ----- docker 镜像加速
  • 原文地址:https://www.cnblogs.com/mendessy/p/11716245.html
Copyright © 2011-2022 走看看