2014 ICPC---Relief grain(树链剖分)

 原题链接

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}.
 
Source
 
题意:有n个村庄面临饥荒,这些村庄由n-1条路连接,构成一棵树,现在政府发放粮食,这些粮食有不同的类型,粮食发放分为m个阶段,每个阶段选择一条路径发放一种类型的粮食,输出每个点发放哪种粮食最多;
 
思路:树链剖分将路径变为线性的,每次对一条路径发放粮食可以转化为在一段连续区间的开始位置加上1,在结束的下一位减1;

        方法就是打标记。线段树维护的是颜色。也就是维护的是[a,b]就是维护a颜色到b颜色某种颜色出现的最多次数。

        假设我们处理的是序列而不是树吧。比如我们要把区间[x,y]图成a颜色.那么我们就在x出加个标记a。在y+1就标记-a。

        多个标记用邻接表连起来就行了。然后从序列的最左端处理到最右端先把所有标记更新到线段树里。a则a颜色+1。

        -a则在线段树将a颜色-1.然后再询问线段树里出现最多的次数就是序列该位置的次数最多的颜色了。相当于递推的思想吧。知道了x位置的颜色线段树.x+1位置的颜色线段树          无非是多了一些颜色或者少了某些颜色。多了减掉。少了的加上就是自己这个位置上的了。这样做之所以高效的原因是标记的是区间的端点而不是区间类的每一个元素。总的          时间复杂度m*log(n)*log(c)。m为询问数。n为结点数。c为颜色种数。

 

我的代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define N 100005
#define M 200005
using namespace std;
int n,q,cnt,sz;
int fa[N][15],deep[N],size[N],head[N];
int pos[N],belong[N];
bool vis[N];
int mv[400005],leaf[100005],h[M];

struct data
{
    int to,next;
} e[M],w[M*4];

void insert(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt;
    e[++cnt].to=u;
    e[cnt].next=head[v];
    head[v]=cnt;
}

void init()
{
    cnt=0;
    sz=0;
        memset(deep,0,sizeof(deep));
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(h,0,sizeof(h));
        memset(mv,0,sizeof(mv));
    for(int i=1; i<n; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        insert(x,y);
    }
}

void dfs1(int x)
{
    size[x]=1;
    vis[x]=1;
    for(int i=1; i<=14; i++)
    {
        if(deep[x]<(1<<i))break;
        fa[x][i]=fa[fa[x][i-1]][i-1];//倍增处理祖先信息
    }
    for(int i=head[x]; i; i=e[i].next)
    {
        if(vis[e[i].to])continue;
        deep[e[i].to]=deep[x]+1;
        fa[e[i].to][0]=x;
        dfs1(e[i].to);
        size[x]+=size[e[i].to];
    }
}

void dfs2(int x,int chain)
{
    int k=0;
    sz++;
    pos[x]=sz;//分配x结点在线段树中的编号
    belong[x]=chain;
    for(int i=head[x]; i; i=e[i].next)
        if(deep[e[i].to]>deep[x]&&size[e[i].to]>size[k])
            k=e[i].to;//选择子树最大的儿子继承重链
    if(k==0)return;
    dfs2(k,chain);
    for(int i=head[x]; i; i=e[i].next)
        if(deep[e[i].to]>deep[x]&&k!=e[i].to)
            dfs2(e[i].to,e[i].to);//其余儿子新开重链
}

void build(int rt,int l,int r)//建线段树
{
    if(l==r)
    {
        leaf[l]=rt;
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
}

void update(int rt,int c)
{
    if(c>0) c=1;
    else    c=-1;
    mv[rt]+=c;
    while(rt>1)
    {
        rt>>=1;
        mv[rt]=max(mv[rt<<1],mv[rt<<1|1]);
    }
}

int qu(int L,int R,int rt)
{
    int ls,rs,mid;
    if(mv[rt]==0)
        return 0;
    while(L<R)
    {
        ls=rt<<1,rs=ls|1,mid=(L+R)>>1;
        if(mv[rs]>mv[ls])
            L=mid+1,rt=rs;
        else
            R=mid,rt=ls;
    }
    return L;
}

void adde(int x,int d)
{
    w[++cnt].to=d;
    w[cnt].next=h[x];
    h[x]=cnt;
}

void uppath(int u,int v,int d)
{
    int f1=belong[u],f2=belong[v];
    while(f1!=f2)
    {
        if(deep[f1]<deep[f2])
            swap(f1,f2),swap(u,v);
        adde(pos[f1],d);
        adde(pos[u]+1,-d);
        u=fa[f1][0],f1=belong[u];
    }
    if(deep[u]>deep[v])
        swap(u,v);
    adde(pos[u],d);
    adde(pos[v]+1,-d);
}

void solve()
{
    build(1,1,100005);
    cnt=0;
    for(int i=1; i<=q; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        uppath(x,y,z);
    }
    int ans[N];
    for(int i=1;i<=n;i++)
    {
        for(int j=h[i];j;j=w[j].next)
        {
            update(leaf[abs(w[j].to)],w[j].to);
        }
        ans[i]=qu(1,100005,1);
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d\n",ans[pos[i]]);
    }
}

int main()
{
    while(scanf("%d%d",&n,&q)&&(n+q))
    {
        init();
        dfs1(1);
        dfs2(1,1);
        solve();
    }
    return 0;
}

 

高手的代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")  
#include <vector>  
#include <stdio.h>  
#include <string.h>  
#include <stdlib.h>  
#include <iostream>  
#include <algorithm>  
using namespace std;  
typedef long long ll;  
typedef unsigned long long ull;  
const int inf=0x3f3f3f3f;  
const ll INF=0x3f3f3f3f3f3f3f3fll;  
const int maxn=100010;  
int fa[maxn],siz[maxn],son[maxn],w[maxn],p[maxn],dep[maxn],fp[maxn],Rank[maxn],ans[maxn];  
//fa为父节点,siz为子节点中siz最大的,dep为深度,son为重儿子,w表示在线段树中的位置  
int num[maxn<<2],ppp[maxn<<2];  
int tree_id,n;  
vector<int>G[maxn];  
void dfs1(int u,int ff,int deep){  
    son[u]=0;fa[u]=ff;siz[u]=1;dep[u]=deep;  
    for(unsigned int i=0;i<G[u].size();i++){  
        int v=G[u][i];  
        if(v==ff) continue;  
        dfs1(v,u,deep+1);  
        siz[u]+=siz[v];  
        if(siz[v]>siz[son[u]]) son[u]=v;  
    }  
}  
void dfs2(int u,int ff){  
    w[u]=++tree_id;p[u]=ff;Rank[w[u]]=u;  
    if(son[u]) dfs2(son[u],ff);  
    else return ;  
    for(unsigned int i=0;i<G[u].size();i++){  
        int v=G[u][i];  
        if(v!=fa[u]&&v!=son[u]) dfs2(v,v);  
    }  
}  
void pushup(int node){  
    if(num[node<<1]>=num[node<<1|1]){  
        num[node]=num[node<<1];ppp[node]=ppp[node<<1];  
    }else{  
        num[node]=num[node<<1|1];ppp[node]=ppp[node<<1|1];  
  
    }  
}  
void buildtree(int le,int ri,int node){  
    if(le==ri){  
        num[node]=0;ppp[node]=le;  
        return ;  
    }  
    int t=(le+ri)>>1;  
    buildtree(le,t,node<<1);  
    buildtree(t+1,ri,node<<1|1);  
    pushup(node);  
}  
void update(int pos,int val,int le,int ri,int node){  
    if(le==ri){  
        num[node]+=val;  
        return ;  
    }  
    int t=(le+ri)>>1;  
    if(pos<=t) update(pos,val,le,t,node<<1);  
    else update(pos,val,t+1,ri,node<<1|1);  
    pushup(node);  
}  
struct nnnn{  
    int u,v,z;  
    nnnn(int a,int b,int c){u=a;v=b;z=c;}  
};  
vector<nnnn>GG;  
vector<int>GGG[maxn];  
void getsum(int u,int v,int z){  
    int f1=p[u],f2=p[v];  
    while(f1!=f2){  
        if(dep[f1]<dep[f2]){  
            swap(f1,f2);  
            swap(u,v);  
        }  
        GG.push_back(nnnn(w[f1],w[u],z));  
        u=fa[f1];f1=p[u];  
    }  
    if(dep[u]>dep[v]) swap(u,v);  
    GG.push_back(nnnn(w[u],w[v],z));  
}  
int main(){  
    int u,v,q,op,z;  
    while(scanf("%d%d",&n,&q)!=-1){  
        if(n==0&&q==0) break;  
        for(int i=0;i<maxn;i++) G[i].clear(),GGG[i].clear();  
        GG.clear();  
        memset(son,0,sizeof(son));tree_id=0;  
        for(int i=0;i<n-1;i++){  
            scanf("%d%d",&u,&v);  
            G[u].push_back(v);  
            G[v].push_back(u);  
        }  
        dfs1(1,1,0);  
        dfs2(1,1);  
        int max1=0;  
        for(int i=1;i<=q;i++){  
            scanf("%d%d%d",&u,&v,&z);  
            max1=max(max1,z);  
            getsum(u,v,z);  
        }  
        if(q==0){  
            for(int i=1;i<=n;i++) printf("0\n");  
            continue;  
        }  
        buildtree(1,max1,1);  
        for(int i=0;i<GG.size();i++){  
            nnnn ne=GG[i];  
            GGG[ne.u].push_back(ne.z);  
            GGG[ne.v+1].push_back(-ne.z);  
        }  
        for(int i=1;i<=n;i++){  
            for(int j=0;j<GGG[i].size();j++){  
                int ttt=GGG[i][j];  
                if(ttt<0) update(-ttt,-1,1,max1,1);  
                else update(ttt,1,1,max1,1);  
            }  
            if(num[1]==0) ans[Rank[i]]=0;  
            else ans[Rank[i]]=ppp[1];  
        }  
        for(int i=1;i<=n;i++) printf("%d\n",ans[i]);  
    }  
    return 0;  
}  

 

posted @ 2016-08-15 20:28  茶飘香~  阅读(250)  评论(0编辑  收藏  举报