hdu 5029 Relief grain (树链剖分+线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5029

题意:给出一棵树,和一种操作。 操作a,b,k相当于将树上a,b结点间的路径上的节点都加上一个k值,最后输出每个结点被加最多次的那个数。

思路:先用树链剖分将问题转化到线性结构上,然后来考虑如何在线性来解决这个问题。 相当于区间染色,最后问每个点被染次数最多的那个颜色。

做法:对于每个操作a,b,k,我们将a节点上标记一个k,b+1节点上标记一个-k,多个不标记我们可以用vector来存储,先将所有的操作都打到这个线性的结构上,再用线段树来维护.

区间a~b上维护的是从颜色a到颜色b出现最多的颜色的标号,这样的话我们可以从左向右来扫描打好标记的线性结构,扫到每个节点都将他所存储的标号更新到线段树上,如果标记

是正值就将对应的颜色的次数+1,否则-1,将所有的标记都传完后,取根节点的值(维护的是整个区间)就能够知道当前被染次数最多的值。 感觉方法十分的巧妙。

code:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <vector>
#include <cstring>
#pragma comment(linker, "/STACK:102400000,102400000") 

using namespace std;

const int maxn=105000;
const int maxe=400010;

struct edge
{
    int to,next;
} P[maxe];

int head[maxn],si;
int fa[maxn],deep[maxn],son[maxn],num[maxn];
int top[maxn],p[maxn],fp[maxn],pos;

void init()
{
    si=0;
    memset(head,-1,sizeof(head));
    pos=1;
    memset(son,-1,sizeof(son));
}

void add_edge(int s,int t)
{
    P[si].to=t;
    P[si].next=head[s];
    head[s]=si++;
}

void dfs1(int u,int pre,int d)
{
    deep[u]=d;
    fa[u]=pre;
    num[u]=1;
    for(int i=head[u];i!=-1;i=P[i].next){
        int v=P[i].to;
        if(v!=pre){
            dfs1(v,u,d+1);
            num[u]+=num[v];
            if(son[u]==-1||num[v]>num[son[u]]) 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=P[i].next){
        int v=P[i].to;
        if(v!=son[u]&&v!=fa[u]) getpos(v,v);
    }

}
vector<int> w[maxn];
void add(int u,int v,int tt)
{
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        //f1 总是表示最深的节点
        w[p[f1]].push_back(tt);
        w[p[u]+1].push_back(-tt);
        u=fa[f1];
        f1=top[u];
    }
    if(deep[u]>deep[v]) swap(u,v);
    w[p[u]].push_back(tt);
    w[p[v]+1].push_back(-tt);
}
//线段树部分
int n,cc[maxn];
struct ppp
{
    int l,r,dat;
} ss[4*maxn+10];

void Build(int k,int ll,int rr)
{
    ss[k].l=ll;
    ss[k].r=rr;
    if(ll==rr){
        ss[k].dat=ll;
        return ;
    }
    else    ss[k].dat=0;
    Build((k<<1)+1,ll,(ll+rr)/2);
    Build((k<<1)+2,(ll+rr)/2+1,rr);
}
void init_tree(int n_)
{
    n=1;
    while(n<n_) n*=2;
    Build(0,0,n-1);
}
void update(int k,int x)
{
    cc[k]+=x;
    k+=n-1;
    while(k>0){
        k=(k-1)/2;
        if(cc[ss[2*k+1].dat]>=cc[ss[2*k+2].dat])
            ss[k].dat=ss[2*k+1].dat;
        else
            ss[k].dat=ss[2*k+2].dat;
    }
}

int ans[maxn];
int main()
{
    int nn,mm,col;
    int s,t,tt;
    while(scanf("%d%d",&nn,&mm),nn!=0||mm!=0){
        init();
        col=0;
        memset(ans,0,sizeof(ans));
        memset(cc,0,sizeof(cc));
        for(int i=1;i<nn;i++){
            scanf("%d%d",&s,&t);
            add_edge(s,t);
            add_edge(t,s);
        }
        dfs1(1,0,0);
        getpos(1,1);
        for(int i=0;i<=nn;i++) w[i].clear();
        while(mm--){
            scanf("%d%d%d",&s,&t,&tt);
            add(s,t,tt);
            col=max(col,tt);
        }
        init_tree(col+1);
        for(int i=1;i<=nn;i++){
            for(int j=0;j<w[i].size();j++){
                if(w[i][j]>0) update(w[i][j],1);
                else          update(-w[i][j],-1);
            }
            ans[fp[i]]=ss[0].dat;
        }
        for(int i=1;i<=nn;i++) printf("%d\n",ans[i]);
    }
    return 0;
}

还有一种方法就是用set,首先定义一个pair,其first值存的是颜色出现的次数,second值存的是对应的颜色,set对应的是先按fisrt排序,其次再按second的排序,就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值