HDU 5029 Relief grain (树链剖分+区间更新)

Relief grain

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others)
Total Submission(s): 3028    Accepted Submission(s): 883


Problem Description
The soil is cracking up because of the drought and the rabbit kingdom is facing a serious famine. The RRC(Rabbit Red Cross) organizes the distribution of relief grain in the disaster area.

We can regard the kingdom as a tree with n nodes and each node stands for a village. The distribution of the relief grain is divided into m phases. For each phases, the RRC will choose a path of the tree and distribute some relief grain of a certain type for every village located in the path.

There are many types of grains. The RRC wants to figure out which type of grain is distributed the most times in every village.
 

Input
The input consists of at most 25 test cases.

For each test case, the first line contains two integer n and m indicating the number of villages and the number of phases.

The following n-1 lines describe the tree. Each of the lines contains two integer x and y indicating that there is an edge between the x-th village and the y-th village.
  
The following m lines describe the phases. Each line contains three integer x, y and z indicating that there is a distribution in the path from x-th village to y-th village with grain of type z. (1 <= n <= 100000, 0 <= m <= 100000, 1 <= x <= n, 1 <= y <= n, 1 <= z <= 100000)

The input ends by n = 0 and m = 0.
 

Output
For each test case, output n integers. The i-th integer denotes the type that is distributed the most times in the i-th village. If there are multiple types which have the same times of distribution, output the minimal one. If there is no relief grain in a village, just output 0.
 

Sample Input
  
  
2 4 1 2 1 1 1 1 2 2 2 2 2 2 2 1 5 3 1 2 3 1 3 4 5 3 2 3 3 1 5 2 3 3 3 0 0
 

Sample Output
  
  
1 2 2 3 3 0 2
Hint
For the first test case, the relief grain in the 1st village is {1, 2}, and the relief grain in the 2nd village is {1, 2, 2}.

 



题意:
给你一棵树,然后有m次运送物资,物资是有种类的,每一次运送给你x,y,z,x是出发点,y是目的地,z是物资种类,沿途的结点会自动获取到z物资
让你输出n行,第i行表示第i个结点接收到物资最多的种类是什么,若没有则输出0,若有许多相同的,输出种类编号最小的那个


题意;
这道题其实跟之前区间更新的那道题差不多 点击打开链接
主要是这道题保存的不是在数组里,而是他的权值线段树上。
因为本来是每一个结点需要建一颗权值线段树来找出现次数最大的种类物资
但这里用区间更新的方法,譬如[u,v]之前运送了一次c(注意这里是指u是树链剖分后的id,不是原来的结点),那么就将sum[u].push(c),sum[v+1].push(-c)
那么这样对于u结点->v结点就只要在u的时候进行将c+1,在之后遍历其他结点中就一直有这个量,直到遍历到v+1再把这个量从权值线段树中剪掉,这样,这n个点就只需要一颗线段树就好了
这里的树链剖分主要是把每一次的运送剖分成一条条id连续的链,这样才能进行后面的可持续化操作

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long int lli;
const int MAXN = 1e5+10;
#define INF 0x3f3f3f3f
#define M(a) memset(a,0,sizeof(a))
#define lson  root<<1
#define rson root<<1|1
int val[MAXN],fa[MAXN],dep[MAXN],siz[MAXN],id[MAXN],top[MAXN],ran[MAXN],son[MAXN];

typedef struct ee
{
    int u,v;
    int next;
    ee(){u=v=next=0;}
    ee(int uu,int vv,int va)
    {
        u=uu;
        v=vv;
        next=va;
    }
}ee;

ee edge[MAXN*2];
int head[MAXN],cnt;
int num,n;

typedef struct node
{
    int l,r;
    int val;
    int type;
}node;

node Btree[MAXN*4];

vector<ee> chain;
vector<int> sum[MAXN];


void addedge(int u,int v)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs1(int x,int f,int d)
{
    dep[x]=d;
    siz[x]=1;
    son[x]=0;
    fa[x]=f;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int tmp=edge[i].v;
        if(tmp==f) continue;
        dfs1(tmp,x,d+1);
        siz[x]+=siz[tmp];
        if(siz[son[x]]<siz[tmp])
        {
            son[x]=tmp;
        }
    }
}

void dfs2(int x,int tp)
{
    top[x]=tp;
    id[x]=++num;
    ran[num]=x;
    if(son[x]) dfs2(son[x],tp);
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int tmp=edge[i].v;
        if(tmp==fa[x]||tmp==son[x]) continue;
        dfs2(tmp,tmp);
    }

}


void pushup(int root)
{
    int ty;
    int maxv;
    if(Btree[lson].val<Btree[rson].val)
        ty=Btree[rson].type,maxv=Btree[rson].val;
    else
        ty=Btree[lson].type,maxv=Btree[lson].val;
    Btree[root].type=ty;
    Btree[root].val=maxv;


}

void build(int l,int r,int root)
{
    if(l>r) return;

    Btree[root].l=l;
    Btree[root].r=r;
    Btree[root].val=0;
    Btree[root].type=0;
    if(l==r)
    {
        Btree[root].type=l;
        return;
    }

    int mid=(l+r)/2;
    build(l,mid,root*2);
    build(mid+1,r,root*2+1);
    pushup(root);
}

void updateone(int root,int index,int val)
{
    if(index==Btree[root].l&&index==Btree[root].r)
    {

        Btree[root].val+=val;
        return;
    }

    int mid=(Btree[root].l+Btree[root].r)/2;
    if(index<=mid)
        updateone(root*2,index,val);
    else
        updateone(root*2+1,index,val);

    pushup(root);

}


void solve1(int a,int b,int c)
{
    if(dep[top[a]]<dep[top[b]])
    {
        swap(a,b);
    }
    while(top[a]!=top[b])
    {

        chain.push_back(ee(id[top[a]],id[a],c));   //!!将一条条查询链拿出来

        a=fa[top[a]];
        if(dep[top[a]]<dep[top[b]])
        {
            swap(a,b);
        }
    }
    if(id[a]>id[b]) swap(a,b);


    chain.push_back(ee(id[a],id[b],c));

}

int res[MAXN];
int main()
{
    int q,t;
    while(scanf("%d%d",&n,&q),n+q)
    {
        num=0;
        M(son);
        M(Btree);  //!! 因为maxv=0的时候Btree保存的就是前面一组数据的树
        cnt=0;
        chain.clear();
        for(int i=1;i<=n+1;i++) sum[i].clear();  //!!!!n+1
        memset(head,-1,sizeof(head));
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        int a,b;
        int c;
        int maxz=0;
        for(int i=0;i<q;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            solve1(a,b,c);
            maxz=max(maxz,c);
        }

        for(int i=0;i<chain.size();i++)
        {
            sum[chain[i].u].push_back(chain[i].next);
            sum[chain[i].v+1].push_back(-chain[i].next);
        }
        for(int i=1;i<=n;i++)
        {
            sort(sum[i].begin(),sum[i].end());   //要先减掉前面的再来加
        }
        build(1,maxz,1);
        for(int i=1;i<=n;i++)   //对图中的每一个节点建一颗权值线段树
        {
            for(int j=0;j<sum[i].size();j++)
            {
                int tmp=sum[i][j];
                if(tmp<0)
                {
                    updateone(1,-tmp,-1);
                }
                else
                {
                    updateone(1,tmp,1);
                }
            }

            res[ran[i]]=Btree[1].type;
            if(Btree[1].val==0) res[ran[i]]=0;

        }

        for(int i=1;i<=n;i++) printf("%d\n",res[i]);


    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值