【JZOJ 3397】雨天的尾巴

Description

深绘里一直很讨厌雨天。

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

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

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

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

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

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

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

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

Solution

首先,我们应该说:

离线大法好!
离线大法好!
离线大法好!

用树链剖分,每种粮食分开来处理
因为题目是一个区间集体+1,所以我们要保证:
如当前区间的标记数组中有数没有下传,那么当前区间的子区间没有被访问,也就是每个区间的标记数组必须把标记传到子区间中被访问的最深处,
这样就可以保证当前区间内所有点只被加了这么多次,这个值也就是当前区间的答案最小值,
当然,我们还要记录最近一次访问当前区间的时间,过时的统计一下当前区间的答案最小值再清零,
全部做完以后再单独跑一遍就可以统计每个点的答案了
复杂度: O(nlog(n)22) ,后面的*2是因为要把每个区间的标记数组必须把标记传到子区间中被访问的最深处。

Code

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
using namespace std;
typedef long long LL;
const int N=100500,maxlongint=2147483640;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m1;
int B[N*2][2],B0,A[N];
struct qqww
{int fa,c,lt,zx,s;}a[N];
int zx[N*5],tim;
struct qww
{int mx,la,ti,ans,ans1;}b[N*5];
struct qwqw {int x,y,z;}sc[N];
int max(int a,int b){return a>b?a:b;}
bool PX(qwqw a,qwqw b){return a.z<b.z;}
void join(int q,int w)
{
    B[++B0][0]=A[q],A[q]=B0,B[B0][1]=w;
    B[++B0][0]=A[w],A[w]=B0,B[B0][1]=q;
}
int dfs1(int q,int c,int fa)
{
    a[q].fa=fa,a[q].c=c;a[q].s=1;
    efo(i,q)if(B[i][1]!=fa)a[q].s+=dfs1(B[i][1],c+1,q);
    return a[q].s;
}
void dfs2(int q,int lt)
{
    if(!lt)lt=q;a[q].lt=lt;zx[a[q].zx=++zx[0]]=q;
    int mx=0;
    efo(i,q)if(B[i][1]!=a[q].fa && a[B[i][1]].s>a[mx].s)mx=B[i][1];
    if(mx)dfs2(mx,lt);
    efo(i,q)if(B[i][1]!=a[q].fa && B[i][1]!=mx)dfs2(B[i][1],0);
}
void clean(int e)
{   
    if(b[e].ti<tim)
    {
        b[e].mx+=b[e].la;
        if(b[e].ans<b[e].mx)b[e].ans=b[e].mx,b[e].ans1=b[e].ti;
        b[e].ti=tim,b[e].la=0,b[e].mx=0;
    }
}
void doit(int e,int cd)
{
    clean(e);
    b[e].mx+=b[e].la;
    if(cd && b[e].la)
    {
        clean(e*2),clean(e*2+1);
        b[e*2].la+=b[e].la;b[e*2].ti=tim;
        b[e*2+1].la+=b[e].la;b[e*2+1].ti=tim;
    }
    b[e].la=0;
}
void change(int l,int r,int e,int l1,int r1,bool l2)
{
    doit(e,r-l);
    if(l==l1 && r==r1)
    {
        b[e].la+=l2;
        doit(e,r-l);
        return;
    }
    int t=(l+r)/2;
    if(r1<=t)change(l,t,e*2,l1,r1,l2);
        else if(t<l1)change(t+1,r,e*2+1,l1,r1,l2);
            else 
            {
                change(l,t,e*2,l1,t,l2);
                change(t+1,r,e*2+1,t+1,r1,l2);
            }
}
void modify(int q,int w,bool e)
{
    int q1,w1;
    while((q1=a[q].lt)!=(w1=a[w].lt))
    {
        if(a[q1].c<a[w1].c)swap(q,w),swap(q1,w1);
        change(1,n,1,a[q1].zx,a[q].zx,e);
        q=a[q1].fa;
    }
    if(a[q].c>a[w].c)swap(q,w);
    change(1,n,1,a[q].zx,a[w].zx,e);
}
void dfssg(int l,int r,int e)
{
    doit(e,r-l);
    if(b[e].ans<b[e/2].ans ||(b[e].ans==b[e/2].ans && b[e].ans1>b[e/2].ans1))b[e].ans=b[e/2].ans,b[e].ans1=b[e/2].ans1;
    if(l==r){A[zx[l]]=b[e].ans1;return;}
    int t=(l+r)/2;
    dfssg(l,t,e*2);
    dfssg(t+1,r,e*2+1);
}
int main()
{
    int q,w;
    read(n);read(m1);
    fo(i,1,n-1)read(q),read(w),join(q,w);
    dfs1(1,1,0);
    dfs2(1,0);
    fo(i,1,m1)read(sc[i].x),read(sc[i].y),read(sc[i].z);
    sort(sc+1,sc+1+m1,PX);
    q=1;
    fo(i,1,m1)
    {
        if(tim!=sc[i].z)
        {
            fo(j,q,i-1)modify(sc[j].x,sc[j].y,0);
            q=i;
        }
        tim=sc[i].z;
        modify(sc[i].x,sc[i].y,1);
    }
    fo(j,q,m1)modify(sc[j].x,sc[j].y,0);
    tim=maxlongint;
    dfssg(1,n,1);
    fo(i,1,n)printf("%d\n",A[i]);
    return 0;
}

你可能感兴趣的:(离线,树链剖分)