HDU 5029 Relief grain (2014年广州赛区网络赛H题)

来源:互联网 发布:李毅吧吧主是谁 知乎 编辑:程序博客网 时间:2024/06/05 19:32

1.题目描述:点击打开链接

2.解题思路:本题是典型的树链剖分题目,不过本题的一个难点在于如何维护每个结点处数量最多的谷物型号。树链剖分经常和离线标记法结合使用,本题亦然。可以用一个vector来维护区间端点处增加的谷物种类。addv[L].push_back(z),subv[R+1].push_back(z)。通过树链剖分可以快速标记好每个区间。接下来就是扫描一遍每个结点,并用一个线段树来维护每个区间上数量最多的那个谷物型号。这样问题即可解决。详细过程请参考代码。

3.代码:

#include<iostream>#include<algorithm>#include<cassert>#include<string>#include<sstream>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>#include<queue>#include<deque>#include<cstdlib>#include<cstdio>#include<cstring>#include<cmath>#include<ctime>#include<cctype>#include<complex>#include<functional>#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;#define me(s)  memset(s,0,sizeof(s))#define rep(i,n) for(int i=0;i<(n);i++)typedef long long ll;typedef unsigned int uint;typedef unsigned long long ull;typedef pair <ll, ll> P;#define lid (id<<1)#define rid (id<<1|1)const int N=100000+20;const int maxo=N*4;struct Edge{    int to;    int next;}edge[N*2];int head[N],top[N],fa[N],son[N],depth[N],num[N];int p[N],fp[N];int tot,pos;vector<int>addv[N];vector<int>subv[N];void init(){    tot=0;pos=1;    memset(head,-1,sizeof(head));    memset(son,-1,sizeof(son));}void addedge(int u,int v){    edge[tot].to=v;    edge[tot].next=head[u];    head[u]=tot++;}void dfs1(int u,int pre,int d){    depth[u]=d;    fa[u]=pre;    num[u]=1;    for(int i=head[u];~i;i=edge[i].next)    {        int v=edge[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 dfs2(int u,int sp){    top[u]=sp;    p[u]=pos++;    fp[p[u]]=u;    if(son[u]==-1)return;    dfs2(son[u],sp);    for(int i=head[u];~i;i=edge[i].next)    {        int v=edge[i].to;        if(v!=son[u]&&v!=fa[u])            dfs2(v,v);    }}void distribute(int u,int v,int z)//给区间[u,v]上都增加一个种类为v的谷物{    while(top[u]!=top[v])    {        if(depth[top[u]]<depth[top[v]])swap(u,v);        addv[p[top[u]]].push_back(z);        subv[p[u]+1].push_back(z);        u=fa[top[u]];    }    if(depth[u]>depth[v])swap(u,v);    addv[p[u]].push_back(z);    subv[p[v]+1].push_back(z);}int maxv[maxo],maxvID[maxo];//maxv[i]表示结点为i的区间最大的谷物数量,maxvID[i]表示结点为i的区间最大谷物数量对应的谷物型号void pushup(int id){    if(maxv[lid]>=maxv[rid])    {        maxv[id]=maxv[lid];        maxvID[id]=maxvID[lid];    }    else    {        maxv[id]=maxv[rid];        maxvID[id]=maxvID[rid];    }}int qx,qv;//qx是当前的谷物型号,qv是增加量void update(int id,int L,int R) {    if(L==qx&&qx==R)    {        maxv[id]+=qv;        maxvID[id]=qx;        return;    }    int M=L+(R-L)/2;    if(qx<=M)update(lid,L,M);    else update(rid,M+1,R);    pushup(id);}int ans[N];int main(){    int n,m;    while(~scanf("%d%d",&n,&m)&&(n||m))    {        init();        for(int i=0;i<n-1;i++)        {            int u,v;            scanf("%d%d",&u,&v);            addedge(u,v);            addedge(v,u);        }        dfs1(1,1,1);        dfs2(1,1);        for(int i=1;i<=pos;i++)            addv[i].clear(),subv[i].clear();        int mz=0; //mz是最大的谷物型号,则线段树的整个区间就是[1,mz]        for(int i=0;i<m;i++)        {            int u,v,z;            scanf("%d%d%d",&u,&v,&z);            mz=max(mz,z);            distribute(u,v,z);        }        me(maxv);        for(int i=1;i<pos;i++)        {            for(int j=0;j<addv[i].size();j++)//计算左端点            {                qx=addv[i][j],qv=1;                update(1,1,mz);            }            for(int j=0;j<subv[i].size();j++)//计算右端点,最终的maxv[1]就是当前结点i处最多的谷物数量,maxvID[1]对应的就是型号            {                qx=subv[i][j],qv=-1;                update(1,1,mz);            }            if(!maxv[1])ans[fp[i]]=0;            else ans[fp[i]]=maxvID[1];        }        for(int i=1;i<=n;i++)            printf("%d\n",ans[i]);    }}

0 0
原创粉丝点击