【JZOJ3397】雨天的尾巴

Description

深绘里一直很讨厌雨天。

灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连

根拔起,以及田地里的粮食被弄得一片狼藉。

无奈的深绘里和村民们只好等待救济粮来维生。

不过救济粮的发放方式很特别。

首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择

两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

Solution

首先所有的操作是已经给定的,询问都是固定的。

我们考虑在序列上做覆盖,在左右端点加1减1,维护最大值即可。

如果在树上,那我们先把树剖成一条条链,再在上面做覆盖即可。时间复杂度O( nlog22 )

可不可以不这么麻烦呢?我们直接考虑树上覆盖。

显然,在左端点 x +1,右端点y+1, lca(x,y) -1, Falca(x,y) -1即可。

于是我们动态开一棵权值线段树,维护每个点最大值的编号。

操作完成后,对于原图每个节点,我们把它与儿子节点合并线段树,对于这题来说,合并就是把个数加起来即可。

这样合并一次是 log2m n 次合并就是nlog2m,所以复杂度是优于树剖的。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 100001
#define M 200001
#define mo 200007
using namespace std;
int to[M],next[M],last[M],num=0;
int d[N],f[N][21];
struct node{
    int l,r;
    int mx;
}tr[N*50];
int a[N];
int h[mo];
int b[N];
int tot=0;
int hash(int x)
{
    int p=x%mo;
    while(h[p] && h[p]!=x) p=(p+1)%mo;
    h[p]=x;
    return p;
}
void link(int x,int y)
{
    num++;
    to[num]=y;
    next[num]=last[x];
    last[x]=num;
}
int cnt=0;
void find(int x)
{
    for(int i=last[x];i;i=next[i])
    {
        int v=to[i];
        if(v!=f[x][0])
        {
            f[v][0]=x;
            d[v]=d[x]+1;
            find(v);
        }
    }
}
int lca(int x,int y)
{
    int tmp=0;
    if(d[x]>d[y]) swap(x,y);
    fd(i,14,0)
    while(d[f[y][i]]>=d[x]) y=f[y][i];
    fd(i,14,0)
    while(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    if(x!=y) x=f[x][0],y=f[y][0];
    return x;
}
void change(int v,int l,int r,int x,int p)
{
    if(l==r && l==x)
    {
        tr[v].mx+=p;
        return;
    }
    int mid=(l+r)/2;
    if(x<=mid)
    {
        if(!tr[v].l) tr[v].l=++tot;
        change(tr[v].l,l,mid,x,p);
    }
    else
    {
        if(!tr[v].r) tr[v].r=++tot;
        change(tr[v].r,mid+1,r,x,p);
    }
    int i=tr[v].l,j=tr[v].r;
    tr[v].mx=max((!i?0:tr[i].mx),(!j?0:tr[j].mx));
}
void merge(int v,int vv,int l,int r)
{
    if(l==r)
    {
        tr[v].mx+=tr[vv].mx;
        return;
    }
    int mid=(l+r)/2;
    if(tr[vv].l)
    {
        if(!tr[v].l) tr[v].l=tr[vv].l;
        else merge(tr[v].l,tr[vv].l,l,mid);
    }
    if(tr[vv].r)
    {
        if(!tr[v].r) tr[v].r=tr[vv].r;
        else merge(tr[v].r,tr[vv].r,mid+1,r);
    }
    int i=tr[v].l,j=tr[v].r;
    tr[v].mx=max((!i?0:tr[i].mx),(!j?0:tr[j].mx));
}
int findans(int v,int l,int r)
{
    if(l==r) return l;
    int mid=(l+r)/2;
    int i=tr[v].l,j=tr[v].r;
    if(tr[i].mx>=tr[j].mx) findans(i,l,mid);
    else findans(j,mid+1,r);
}
int lyd=0;
void dfs(int x)
{
    for(int i=last[x];i;i=next[i])
    {
        int v=to[i];
        if(v!=f[x][0])
        {
            dfs(v);
            merge(a[x],a[v],1,mo-1);
        }
    }
    b[x]=findans(a[x],1,mo-1);
}
int main()
{
    int n,m;
    cin>>n>>m;
    fo(i,1,n-1)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        link(x,y);
        link(y,x);
    }
    find(1);
    fo(j,1,20)
    fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
    fo(i,1,n) a[i]=i;
    tot=n;
    d[0]=-1;
    fo(i,1,m)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        int p=hash(z);
        change(a[x],1,mo-1,z,1);
        change(a[y],1,mo-1,z,1);
        int t=lca(x,y);
        change(a[t],1,mo-1,z,-1);
        change(a[f[t][0]],1,mo-1,z,-1);
    }
    dfs(1);
    fo(i,1,n) printf("%d\n",h[b[i]]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值