树链剖分+线段树+离线(广州网选赛第八题hdu5029)

http://acm.hdu.edu.cn/showproblem.php?pid=5029

Relief grain

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


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

题意:给出一棵树,然后有一系列的操作,每次操作有a,b,c代表在a到b的路径上的每个点都给一个c类型的数字;最后问所有操作完成后树上的每个节点拥有的数量最多的类型是多少,如果存在多个输出较小类型的数字;

分析:首先这种在树上的操作,很容易想到是树链剖分的题目,但是具体该怎么写呢:先不考虑树的问题,可以把题意抽象出一个普通的连续区间【1,n】,然后在不同区间进行分配东西,最后问每个点的结果,对于区间【L,R】上分配一个数字Z,那么就在L位置上标记+Z,代表从此时以后的每个节点都拥有一个Z,然后在R+1的位置上标记一个-Z,代表从该节点开始没有分配Z这个数字;所以先把m个操作记录下来,对于每个标记的节点,可以用一个vector容器记录所标记的东西,接下来我们可以建立一个权值线段树区间【1,100000】;每个点分别代表数字Z的类型,px数组代表每个类型数字当前出现的次数;tree[i].maxi代表i区间的最大值,从左向右遍历区间【1,n】,对于每个节点更新px的值,如果标记是+Z,则px[z]++;否则px[-z]--;然后query即可;

树链剖分也是同样的道理,轻重链的划分不详说了,对于一个树上的两点<u,v>,把它转化为多条连续的子链利用线段树操作即可;具体程序如下:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include"stdio.h"
#include"string.h"
#include"iostream"
#include"map"
#include"string"
#include"queue"
#include"stdlib.h"
#include"math.h"
#define M 110009
#define eps 1e-10
#define inf 1000000000
#define mod 1000000000
#define INF 1000000000
using namespace std;
struct node
{
    int u,v,next;
}edge[M*2];
struct Lnode
{
    int v;
    Lnode(int vv)
    {
        v=vv;
    }
};
vector<Lnode>e[M];
int t,pos,head[M],son[M],fa[M],num[M],top[M],ID,deep[M],p[M],fp[M],px[M];
void init()
{
    t=pos=0;
    memset(head,-1,sizeof(head));
    memset(son,-1,sizeof(son));
    memset(fa,-1,sizeof(fa));
}
void add(int u,int v)
{
    edge[t].u=u;
    edge[t].v=v;
    edge[t].next=head[u];
    head[u]=t++;
}
void dfs(int u,int f,int d)
{
    deep[u]=d;
    fa[u]=f;
    num[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==f)continue;
        dfs(v,u,d+1);
        num[u]+=num[v];
        if(son[u]==-1||num[son[u]]<num[v])
            son[u]=v;
    }
}
void getpos(int u,int sp)
{
    top[u]=sp;
    p[u]=pos++;
    fp[p[u]]=u;
    if(son[u]==-1)return;
    getpos(son[u],sp);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==fa[u]||v==son[u])continue;
        getpos(v,v);
    }
}
/**********以上是划分轻重链**************/
struct Tree
{
    int l,r,maxi;
}tree[M*4];
void make(int l,int r,int i)//线段树建树操作
{
    tree[i].l=l;
    tree[i].r=r;
    if(l==r)
    {
        tree[i].maxi=0;
        return ;
    }
    int mid=(l+r)/2;
    make(l,mid,i*2);
    make(mid+1,r,i*2+1);
    tree[i].maxi=max(tree[i*2].maxi,tree[i*2+1].maxi);
}
void updata(int p,int q,int i)//单点更新操作
{
    if(tree[i].l==p&&tree[i].r==p)
    {
        tree[i].maxi=q;
        return;
    }
    int mid=(tree[i].l+tree[i].r)/2;
    if(p<=mid)updata(p,q,i*2);
    else updata(p,q,i*2+1);
    tree[i].maxi=max(tree[i*2].maxi,tree[i*2+1].maxi);
}
void query(int l,int r,int i)//区间查找最大值对应的标,若有多个查找编号最小的记录到ID中
{
    if(tree[i].maxi<tree[1].maxi)
        return;
    if(tree[i].l==tree[i].r)
    {
        if(tree[i].l<ID)
            ID=tree[i].l;
        return;
    }
    int mid=(tree[i].l+tree[i].r)/2;
    if(r<=mid)
        query(l,r,i*2);
    else if(l>mid)
        query(l,r,i*2+1);
    else
    {
        query(l,mid,i*2);
        query(mid+1,r,i*2+1);
    }
}
void Insert(int u,int v,int w)//树与线段树的转化操作
{
    int f1=top[u];
    int f2=top[v];
    while(f1!=f2)
    {
        if(deep[f1]<deep[f2])
        {
            swap(f1,f2);
            swap(u,v);
        }
        e[p[f1]].push_back(Lnode(w));
        e[p[u]+1].push_back(Lnode(-w));
        u=fa[f1];
        f1=top[u];
    }
    if(u==v)
    {
        e[p[u]].push_back(Lnode(w));
        e[p[u]+1].push_back(Lnode(-w));
        return;
    }
    if(deep[u]>deep[v])
        swap(u,v);
    e[p[u]].push_back(Lnode(w));
    e[p[v]+1].push_back(Lnode(-w));
    return;
}
int s[M];
int main()
{
    int n,m,i,j,a,b,c,u,v;
    while(scanf("%d%d",&n,&m),m||n)
    {
        init();
        for(i=0;i<=n;i++)
            e[i].clear();
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs(1,1,1);
        getpos(1,1);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            Insert(a,b,c);
        }
        make(1,100000,1);
        memset(px,0,sizeof(px));
        for(i=0;i<pos;i++)
        {
            for(j=0;j<(int)e[i].size();j++)
            {
                int v=e[i][j].v;
                if(v>0)
                {
                    px[v]++;
                    updata(v,px[v],1);
                }
                else
                {
                    px[-v]--;
                    updata(-v,px[-v],1);
                }
            }
            if(tree[1].maxi==0)
            {
                 s[fp[i]]=0;
                 continue;
            }
            ID=100000;
            query(1,100000,1);
            s[fp[i]]=ID;
            //printf("%d\n",ID);
        }
        for(i=1;i<=n;i++)
            printf("%d\n",s[i]);
    }
    return 0;
}



转载于:https://www.cnblogs.com/mypsq/p/4348149.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值