GDOI2014模拟雨天的尾巴

Posted 无尽的蓝黄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GDOI2014模拟雨天的尾巴相关的知识,希望对你有一定的参考价值。

题目

深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连
根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。
首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择
两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

分析

相信大多数人都跟我一样,看到这道题,果断认为是树链剖分。
其实不然(我1.5h把熟练剖分打出来,以为能过50%。结果打错了,20%。然后。。。打暴力的人都是50%,#%……&*)。
我们注意到z<=10^9,那么的数据范围。
但是m<=100000,所以就先做个离散化。
。。。
接着用到个很神奇的东西,叫线段树合并。
对于树上的每一个节点开一棵线段树,记录该节点的每个z的数量以及某一段z的最大值。注意要动态开节点,否则会爆空间。
发现,如果要修改从
xy的路径,其实就是将x的z值加一,y的z值加一,lca(x,y)的z值减一以及fa[lca(x,y)]的z值减一(why?因为当线段树合并后从xlca(x,y)的路径上和从ylca(x,y)的路径上的每个节点的z值都加一,但lca(x,y)这个节点重复加了,那么就减掉。不过剩余的z值还会继续上传,所以在fa[lca(x,y)]**就把上传的z值减去)
合并操作

int mesh(int x,int y,int l,int r)
{
    if(l==r)
    {
        tree[x].v+=tree[y].v;
        return 0;                                                    
    }
    int mid=(l+r)/2;
    if(tree[y].l)
    {
        if(!tree[x].l)
            tree[x].l=tree[y].l;
            else
        mesh(tree[x].l,tree[y].l,l,mid);
    }
    if(tree[y].r)
    {
        if(!tree[x].r)
            tree[x].r=tree[y].r;
            else
        mesh(tree[x].r,tree[y].r,mid+1,r);
    }
    tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}

当然,当所有的修改操作做完后才线段树合并,否则会超时。


#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
using namespace std;
struct trees
{
    int l,r,v;
}tree[8000000];
struct read
{
    int x,y,z;
}re[110000];
int next[201000],last[201000],to[201000],po,tot,n,m,g[200000][20],t[200000],deep[200000],f[200000],sum,ans[2000000];
bool cmp(read x,read y)
{
    return x.z<y.z;
}
int bj(int x,int y)
{
    next[++tot]=last[x];
    last[x]=tot;
    to[tot]=y;
}
int dg(int x)
{
    for(int i=last[x];i;i=next[i])
    {
        int j=to[i];
        if(j!=g[x][0])
        {
            deep[j]=deep[x]+1;
            g[j][0]=x;
            dg(j);
        }
    }
}
int prelca()
{
    for(int j=1;j<=log2(n);j++)
    {
        for(int i=1;i<=n;i++)
        {
            g[i][j]=g[g[i][j-1]][j-1];
        }
    }
}
int lca(int x,int y)
{
    if(deep[x]>deep[y])
    {
        x=x^y;
        y=x^y;
        x=x^y;
    }
    for(int i=log2(n);i>=0;i--)
    {
        if(deep[g[y][i]]>deep[x])
            y=g[y][i];
    }
    if(deep[y]!=deep[x]) y=g[y][0];
    for(int i=log2(n);i>=0;i--)
    {
        if(g[y][i]!=g[x][i])
        {
            y=g[y][i];
            x=g[x][i];
        }
    }
    if(x!=y) y=g[y][0];
    return y;
}
int put(int v,int l,int r,int x,int y)
{
    if(l==r)
    {
        tree[v].v+=y;
        return 0;
    }
    int mid=(l+r)/2;
    if(x<=mid)
    {
        if(!tree[v].l)
            tree[v].l=++sum;
        put(tree[v].l,l,mid,x,y);
    }
    else
    {
        if(!tree[v].r)
            tree[v].r=++sum;
        put(tree[v].r,mid+1,r,x,y); 
    }
    tree[v].v=max(tree[tree[v].l].v,tree[tree[v].r].v);
}
int work(int x,int y,int z)
{
    int lc=lca(x,y);
    if(!f[x])
    {
        f[x]=++sum;
    }
    put(f[x],1,tot,z,1);
    if(!f[y])
    {
        f[y]=++sum;
    }
    put(f[y],1,tot,z,1);
    if(!f[lc])
    {
        f[lc]=++sum;
    }
    put(f[lc],1,tot,z,-1);
    if(g[lc][0])
    {
        if(!f[g[lc][0]]) f[g[lc][0]]=++sum;
        put(f[g[lc][0]],1,tot,z,-1);
    }
}
int mesh(int x,int y,int l,int r)
{
    if(l==r)
    {
        tree[x].v+=tree[y].v;
        return 0;                                                    
    }
    int mid=(l+r)/2;
    if(tree[y].l)
    {
        if(!tree[x].l)
            tree[x].l=tree[y].l;
            else
        mesh(tree[x].l,tree[y].l,l,mid);
    }
    if(tree[y].r)
    {
        if(!tree[x].r)
            tree[x].r=tree[y].r;
            else
        mesh(tree[x].r,tree[y].r,mid+1,r);
    }
    tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}
int find(int v,int l,int r)
{
    if(l==r)
    {
        return l;
    }
    if(tree[tree[v].l].v==0 && tree[tree[v].r].v==0)
        return 0;
    int mid=(l+r)/2;
    if(tree[tree[v].l].v>=tree[tree[v].r].v)
    {
        return find(tree[v].l,l,mid);
    }
    else
    {
        return find(tree[v].r,mid+1,r); 
    }
}
int merge(int x)
{
    for(int i=last[x];i;i=next[i])
    {
        int j=to[i];
        if(j!=g[x][0])
        {
            merge(j);
            if(!f[x])
            {       
                f[x]=++sum;
            }   
            if(!f[j])
            {       
                f[j]=++sum;
            }
            mesh(f[x],f[j],1,tot);
        }
    }
    ans[x]=find(f[x],1,tot);
}
int main()
{
    scanf("%d",&n);
    scanf("%d",&m);
    for(int i=1;i<=n-1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        bj(x,y);
        bj(y,x);
    }
    deep[1]=1;
    dg(1);
    prelca();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&re[i].x,&re[i].y,&re[i].z);
    }
    sort(re+1,re+m+1,cmp);
    tot=0;
    sum=0;
    for(int i=1;i<=m;i++)
    {
        if(re[i].z==t[tot])
        {
            re[i].z=tot;
        }
        else
        {
            t[++tot]=re[i].z;
            re[i].z=tot;
        }
    }
    for(int i=1;i<=m;i++)
    {
        work(re[i].x,re[i].y,re[i].z);
    }
    merge(1);
    for(int i=1;i<=n;i++)
    {
        if(!f[i])
        {       
            f[i]=++sum;
        }   
        printf("%d\n",t[ans[i]]);
    }
}

以上是关于GDOI2014模拟雨天的尾巴的主要内容,如果未能解决你的问题,请参考以下文章

GDOI模拟雨天的尾巴树套树

bzoj3307:雨天的尾巴

[bzoj3307]雨天的尾巴_线段树合并

雨天的尾巴

Luogu4556 雨天的尾巴 树上差分线段树合并

P4556 [Vani有约会]雨天的尾巴 树链剖分 线段树合并