雨天的尾巴

首先村落里的一共有n座房屋,并形成一个树状结构。然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x
到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮。然后深绘里想知道,当所有的救济粮发放完毕后,每
座房子里存放的最多的是哪种救济粮。
1 <= n, m <= 100000, 1 <= a, b, x, y <= n, 1 <= z <= 100000

//针对每个给出的树上的点,建立一个权值线段树,动态开点
//权值线段树的叶子点,代表这个树上的点所存放的东西,及数量
 
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=100000+2;
const int U=100001;
int n,m;
struct haha
{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y)
{
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
	hd[x]=cnt;
}
int fa[N][18];
int dep[N];
int ans[N];
void dfs(int x,int d)
{
    dep[x]=d;//x的深度为d,根结点深度为1 
    for(int i=hd[x];i;i=e[i].nxt)
	{
        int y=e[i].to;
		if(y==fa[x][0]) 
		    continue;
        fa[y][0]=x;
		dfs(y,d+1);
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) 
	   swap(x,y);
    for(int j=17;j>=0;j--)
	{
        if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
    }
    if(x==y) return x;
    for(int j=17;j>=0;j--)
	{
        if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
    }
	return fa[x][0];
}
struct node
{
    int mx,id;
    int ls,rs;
}t[N*60];
int tot;
int rt[N];
void pushup(int x)//上传标记 
//mx最多数字的个数
//id最多数字是哪一种 
{
    if(t[x].ls&&t[x].rs)//如果有左右结点 
	{
        t[x].mx=max(t[t[x].ls].mx,t[t[x].rs].mx);
        t[x].id=t[t[x].ls].mx>=t[t[x].rs].mx?t[t[x].ls].id:t[t[x].rs].id;
    }
    else 
	    if(t[x].ls)
		{
                t[x].mx=t[t[x].ls].mx;
                t[x].id=t[t[x].ls].id;
        }
        else 
		    if(t[x].rs)
			{
                    t[x].mx=t[t[x].rs].mx;
                    t[x].id=t[t[x].rs].id;
            }
}
void upda(int &x,int l,int r,int p,int c)
//upda(rt[x],1,U,z,1);
//x代表结点编号,l,r代表范围
//p代表加入哪一种货物 
//c代表所加的数量 
{
    
    if(!x) 
	   x=++tot;
    if(l==r) 
	{
        t[x].id=l; //在线段树中编号为x的结点,所存放食品的种类 
		t[x].mx+=c;//存放食品的数量 
        return;
    }
    if(p<=mid) //转到左子树更新 
	    upda(t[x].ls,l,mid,p,c);
    else //转到右子树更新
	     upda(t[x].rs,mid+1,r,p,c);
    pushup(x);
}
int merge(int x,int y,int l,int r)
{
    if(!x||!y)
	//哪个点不为0就返回哪个点的编号,如果同时为0就返回0 
	//如果此处没有返回,则说明合并的结点不为空 
	    return x|y;
    if(l==r)//到了叶子点,强行合并 
	{
        t[x].mx+=t[y].mx;
    }
    else
	{
              t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
			  //左子树与左子树合并,返回得是结点的编号 
              t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);
			  //右子树与右子树合并 
              pushup(x);//标记上传 
    }
    return x;
}
void sol(int x)
//求出x点存放的物品,哪种编号的最多 
{
    for(int i=hd[x];i;i=e[i].nxt)
	{
        int y=e[i].to;
        if(y==fa[x][0]) continue;
        sol(y);
        rt[x]=merge(rt[x],rt[y],1,U);//与其子结点进行合并 
     
    }
    if(t[rt[x]].mx>0)
	   ans[x]=t[rt[x]].id;
}
int main()
{
    scanf("%d%d",&n,&m);int x,y;
    for(int i=1;i<=n-1;i++)
	{
        scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
    }
	dfs(1,1);
    dep[0]=-1;
    for(int j=1;j<=17;j++)
        for(int i=1;i<=n;i++)
             fa[i][j]=fa[fa[i][j-1]][j-1];
    int z;
    while(m--)
	{
        scanf("%d%d%d",&x,&y,&z);
        int anc=lca(x,y);
        upda(rt[x],1,U,z,1); //对x这个端点,加1 
        upda(rt[y],1,U,z,1);//对y这个端点,加1
        upda(rt[anc],1,U,z,-1);//对lca减1 
        if(fa[anc][0])  //如果lca有父亲点,也要减1  
		    upda(rt[fa[anc][0]],1,U,z,-1);
        
    }
    sol(1);
    for(int i=1;i<=n;i++)
	{
        printf("%d\n",ans[i]);
    }
	return 0;
}

  

posted @ 2019-11-11 15:40  我微笑不代表我快乐  阅读(115)  评论(0编辑  收藏  举报