【扫描线+线段树】BZOJ5392 路径统计 [BZOJ2018JUN月赛]

【题目】

原题地址
大意:求有多少个树上点对 (u,v) ( u , v ) 满足这条路径上经过的所有点排序后恰好为连续的一段,这里 (u,v)(v,u) ( u , v ) 和 ( v , u ) 算作同一情况

这里写图片描述

【解题思路】
这道题目的思想挺妙的。
我们很容易可以得到一个 O(nlog2n) O ( n l o g 2 n ) 的常数比较大的做法,但很可惜这题过不了。
简单说一下:考虑一条序列上我们如何处理这个问题,发现 [l,r] [ l , r ] 满足条件的充要条件是 max[l,r]min[l,r]=rl+1 m a x [ l , r ] − m i n [ l , r ] = r − l + 1 ,同时若将字母及常数全部移到左边,柿子的值恒>=0。
然后就可以通过维护最小值及个数来得到答案。

对于这道题我们考虑将序列问题应用到树上,采用点分治的手段。
对于到当前重心,我们记录每个点的 dep d e p 和路径上的 min m i n ,路径上的 max m a x
这里的柿子变成 maxmin=depx+depy1 m a x − m i n = d e p x + d e p y − 1 可化为 maxmindepxdepy+1>=0 m a x − m i n − d e p x − d e p y + 1 >= 0
接着讲每个点按照 min m i n 排序,并再开一个数组将点按 max m a x 排序。
接下来将每个 min m i n 插入线段树,然后按 max m a x 从大到小在线段树上查询,这样可以保证我们得到一个最大值。设当前考虑点为 x x
考虑对一条路径,如果我们取到的最小值在根到x上,那么就是在线段树中查找 depy d e p y 的最大值使得柿子最小(不用考虑 min m i n 在根到 y y 路径上,因为如果min在根到 y y 路径上,显然柿子的值大于0)
如果min在根到 y y 路径上,那么我们要使柿子的值尽量小,就是让min+depy尽量大,这个就是我们要在线段树上维护的东西。
做完一个节点后将这个点从线段树上删去即可。

接下来是正解的做法:
我们设一个函数 f(x,y) f ( x , y ) 表示 x x y的路线上 [(w,w+1)] [ 经 过 边 数 − ( w , w + 1 ) 同 时 存 在 的 对 数 ] ,显然如果一条路线是合法的,它的f为0.
分别考虑每条树边和数对对 f f 的贡献,则每条路径的贡献可以在二维平面上表示为2个或4个矩形。
具体是这样的:对于一条路径(u,v),如果 lca(u,v) l c a ( u , v ) 不是这两个点,则贡献的路径是在两个点的子树中分别取一个点。
否则,设 u u v的祖先, w w u v v 路径上的第一个点,则贡献的路径是在v的子树中取一个点,再在不是 w w 的子树中取一个点。
上面的每一个”取”就对应dfs序上的一段或两段区间,取的两个点分别作为 x x y
注意我们的每个矩形,我们要将它的 x x y交换后作为一个新统计答案矩形放进去,这样才能统计答案。
然后就可以用扫描线+线段树来做了,每次查询0的个数即可(最小值)。
最后的答案是我们上面得出的答案 ans+n2 a n s + n 2

实现参照代码,原来写的直接丢矩形,常数极大没有过。优化扫描线以后勉强能在BZOJ上过去,修改前和修改后的部分在代码中都有体现。

【参考代码】

#include<bits/stdc++.h>
#define ls (x<<1)
#define rs (x<<1|1)
using namespace std;

typedef long long LL;
const int INF=1e9;
const int N=3e5+10;
const int M=N*4;
int n,cnt,ind,tot;
int fc[25],head[N],dep[N],beg[N],en[N];
int fa[24][N];
LL ans;

int read()
{
    int ret=0,f=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
    while(isdigit(c)) {ret=(ret<<1)+(ret<<3)+(c^48);c=getchar();}
    return f?ret:-ret;
}

struct Tway
{
    int v,nex;
};
Tway e[N<<1];

void add(int u,int v)
{
    e[++tot]=(Tway){v,head[u]};head[u]=tot;
    e[++tot]=(Tway){u,head[v]};head[v]=tot;
}

struct Tmat
{
    int x,y,dy,ad/*,opt*/;
    Tmat(){}
//  Tmat(int xx,int yy,int dyy,int da,int tt){x=xx;y=yy;dy=dyy;ad=da;opt=tt;}
    Tmat(int xx,int yy,int dyy,int da){x=xx;y=yy;dy=dyy;ad=da;}
};
Tmat a[N<<4];

bool cmp(Tmat A,Tmat B)
{
//  if(A.x==B.x)
//      return A.opt<B.opt;
    return A.x<B.x;
} 

struct Segment
{
    int mi[M],minum[M],tar[M];

    void pushup(int x)
    {
        if(mi[ls]==mi[rs])
            mi[x]=mi[ls],minum[x]=minum[ls]+minum[rs];
        else
        if(mi[ls]<mi[rs])
            mi[x]=mi[ls],minum[x]=minum[ls];
        else
            mi[x]=mi[rs],minum[x]=minum[rs];
    }

    void pushdown(int x)
    {
        mi[ls]+=tar[x];mi[rs]+=tar[x];
        tar[ls]+=tar[x];tar[rs]+=tar[x];
        tar[x]=0;
    }

    void build(int x,int l,int r)
    {
        if(l==r)
        {
            mi[x]=0;minum[x]=1;tar[x]=0;    
            return;
        }   
        int mid=(l+r)>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        pushup(x);
    }

    void update(int x,int l,int r,int L,int R,int v)
    {
        if(L<=l && r<=R)
        {
            mi[x]+=v;tar[x]+=v;
            return;
        }
        pushdown(x);
        int mid=(l+r)>>1;
        if(L<=mid) update(ls,l,mid,L,R,v);
        if(R>mid) update(rs,mid+1,r,L,R,v);
        pushup(x);
    }
}tr;

void dfs(int x,int f)
{
    beg[x]=++ind;
    for(int i=1;i<23;++i)
        fa[i][x]=fa[i-1][fa[i-1][x]];
    for(int i=head[x];i;i=e[i].nex)
    {
        int v=e[i].v;
        if(v==f)
            continue;
        fa[0][v]=x;dep[v]=dep[x]+1;
        dfs(v,x);
    }
    en[x]=ind;
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=0,t=dep[x]-dep[y];t;++i)
        if(t&fc[i])
            x=fa[i][x],t^=fc[i];
    for(int i=22;~i;--i)
        if(fa[i][x]^fa[i][y])
            x=fa[i][x],y=fa[i][y];
    return x==y?x:fa[0][x];
}

void dfs2(int x)
{
    for(int i=head[x];i;i=e[i].nex)
    {
        int v=e[i].v;
        if(v==fa[0][x])
            continue;
/*      a[++cnt]=Tmat(beg[v],1,n,1,1);a[++cnt]=Tmat(en[v],1,n,-1,2);
        a[++cnt]=Tmat(beg[v],beg[v],en[v],-1,1);a[++cnt]=Tmat(en[v],beg[v],en[v],1,2);
        a[++cnt]=Tmat(1,beg[v],en[v],1,1);a[++cnt]=Tmat(n,beg[v],en[v],-1,2);
        a[++cnt]=Tmat(beg[v],beg[v],en[v],-1,1);a[++cnt]=Tmat(en[v],beg[v],en[v],1,2);*/
        a[++cnt]=Tmat(beg[v],1,n,1);a[++cnt]=Tmat(beg[v],beg[v],en[v],-1);
        if(en[v]+1<=n) a[++cnt]=Tmat(en[v]+1,1,n,-1),a[++cnt]=Tmat(en[v]+1,beg[v],en[v],2);
        a[++cnt]=Tmat(1,beg[v],en[v],1);a[++cnt]=Tmat(beg[v],beg[v],en[v],-1);
        dfs2(v);
    }
}

void init()
{
    fc[0]=1; for(int i=1;i<=23;++i) fc[i]=fc[i-1]<<1;
    n=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read();
        add(u,v);
    }
    dfs(dep[1]=1,0);
    dfs2(1);
    for(int i=1;i<n;++i)
    {
        int f=lca(i,i+1);
        if(f!=i && f!=i+1)
        {
/*          a[++cnt]=Tmat(beg[i],beg[i+1],en[i+1],-1,1);a[++cnt]=Tmat(en[i],beg[i+1],en[i+1],1,2);
            a[++cnt]=Tmat(beg[i+1],beg[i],en[i],-1,1);a[++cnt]=Tmat(en[i+1],beg[i],en[i],1,2);*/
            a[++cnt]=Tmat(beg[i],beg[i+1],en[i+1],-1);a[++cnt]=Tmat(beg[i+1],beg[i],en[i],-1);
            if(en[i]+1<=n) a[++cnt]=Tmat(en[i]+1,beg[i+1],en[i+1],1);
            if(en[i+1]+1<=n) a[++cnt]=Tmat(en[i+1]+1,beg[i],en[i],1);
        }
        else
        {
            int x=(f==i?i:i+1),y=(f==i?i+1:i),z=y;
            for(int j=0,t=dep[y]-dep[x]-1;t;++j)
                if(t&fc[j])
                    t^=fc[j],z=fa[j][z];
/*          a[++cnt]=Tmat(beg[y],1,n,-1,1);a[++cnt]=Tmat(en[y],1,n,1,2);
            a[++cnt]=Tmat(beg[y],beg[z],en[z],1,1);a[++cnt]=Tmat(en[y],beg[z],en[z],-1,2);
            a[++cnt]=Tmat(1,beg[y],en[y],-1,1);a[++cnt]=Tmat(n,beg[y],en[y],1,2);
            a[++cnt]=Tmat(beg[z],beg[y],en[y],1,1);a[++cnt]=Tmat(en[z],beg[y],en[y],-1,2);*/
            a[++cnt]=Tmat(beg[y],1,n,-1);a[++cnt]=Tmat(beg[y],beg[z],en[z],1);
            if(en[y]+1<=n) a[++cnt]=Tmat(en[y]+1,1,n,1),a[++cnt]=Tmat(en[y]+1,beg[z],en[z],-1);
            a[++cnt]=Tmat(1,beg[y],en[y],-1);a[++cnt]=Tmat(beg[z],beg[y],en[y],1);
            if(en[z]+1<=n) a[++cnt]=Tmat(en[z]+1,beg[y],en[y],-1);
        }
    }
}

void solve()
{
    tr.build(1,1,n);
    sort(a+1,a+cnt+1,cmp);
    int nowl=1,nowr=1;
    for(int i=1;i<=n;++i)
    {
/*      if(a[nowl].x^i)
        {
            if(!tr.mi[1]) ans+=tr.minum[1];
            continue;
        }
        if(a[nowl].opt==1)
            tr.update(1,1,n,a[nowl].y,a[nowl].dy,a[nowl].ad);
        while(a[nowr+1].x==a[nowl].x && a[nowr+1].opt==1)
            ++nowr,tr.update(1,1,n,a[nowr].y,a[nowr].dy,a[nowr].ad);
        if(!tr.mi[1]) ans+=tr.minum[1];
        if(a[nowl].opt==2)
            tr.update(1,1,n,a[nowl].y,a[nowl].dy,a[nowl].ad);
        while(a[nowr+1].x==a[nowl].x) 
            ++nowr,tr.update(1,1,n,a[nowr].y,a[nowr].dy,a[nowr].ad);*/
        if(a[nowl].x^i)
        {
            if(!tr.mi[1]) ans+=tr.minum[1];
            continue;
        }
        tr.update(1,1,n,a[nowl].y,a[nowl].dy,a[nowl].ad);
        while(a[nowr+1].x==a[nowl].x)
            ++nowr,tr.update(1,1,n,a[nowr].y,a[nowr].dy,a[nowr].ad);
        if(!tr.mi[1]) ans+=tr.minum[1];
        nowl=nowr+1;nowr=nowl;
    }
    ans=(ans+n)/2ll;
    printf("%lld\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("BZOJ5392.in","r",stdin);
    freopen("BZOJ5392.out","w",stdout);
#endif
    init();
    solve();

    return 0;
}

【总结】
这个转换函数的思想很好用,事实上对于某些树上问题,我们也可以将可行点域映射到二维平面上去解决问题。
然后就是这题的常数问题,很多时候重复或无用的东西应当优化掉,不然可能会被卡成**

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值