【JZOJ3397】雨天的尾巴

来源:互联网 发布:mysql 死锁 编辑:程序博客网 时间:2024/04/20 15:17

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即可。

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

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

这样合并一次是log2mn次合并就是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 200007using 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
原创粉丝点击