其他分享
首页 > 其他分享> > [机房测试]超级加倍

[机房测试]超级加倍

作者:互联网

Description

给定一棵树。
我们认为一条从 \(x\to y\) 的简单路径是好的,当且仅当路径上的点中编号最小的是 \(x\),最大的是 \(y\)。
请求出好的简单路径条数。

Solution

先考虑序列上怎么做,可以考护两个单调栈,查询的时候 upper_bound 一下。但是一个更直接的做法是建出两棵笛卡尔树,一个大根一个小根,那么满足条件的点对在两棵树中一定互为祖父关系,那么把询问差分之后就是一个经典二维偏序问题。类比于序列上的问题,在树上一个类似笛卡尔树的结构是 Kruskal 重构树。与边的重构树不同,点的重构树不是二叉树,但是满足重构树上任意两点的 Lca 一定是原树上两点路径上的权值最小(最大)值的节点。树的构建可以考虑从小到大加点,遍历所有与当前点相连的且已经在重构树中的连通块,将该连通块的父亲指向当前点。之后的问题就和序列上一样了。

很卡常,不要用 vector,实测同样复杂度,邻接表满分,vector 75。

#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

const int N=2e6+7;

vector<int> g[N];

int c[N],n,m;
inline int lowbit(int x){return -x&x;}
inline void add(int x,int v){while(x<=n)c[x]+=v,x+=lowbit(x);}
inline int query(int x,int ret=0){
    while(x)ret+=c[x],x-=lowbit(x);return ret;
}

struct Tree{
    vector<int> e[N];
    int fa[N],Fa[N],in[N],out[N],timer=0;
    inline int find(int x){
        if(fa[x]==x) return x;
        return fa[x]=find(fa[x]);
    }
    void dfs(int u){
        in[u]=++timer;
        for(int v:e[u]) dfs(v);
        out[u]=timer;
    }
    void work(){
        for(int i=1;i<=n;i++)
            e[Fa[i]].push_back(i);
        dfs(find(1));
    }
}T1,T2;

struct Seg{
    int pos,l,r;
    bool operator <(const Seg &X) const{
        return pos<X.pos;
    }
}s[N];


struct Que{
    int pos,x,op;
    bool operator <(const Que &X) const{
        return pos<X.pos;
    }
}q[N*2];

int main(){
    freopen("charity.in","r",stdin);
    freopen("charity.out","w",stdout);
    n=read(); int x=read();
    for(int i=2;i<=n;i++){
        x=read();
        g[x].push_back(i);
        g[i].push_back(x);
    }
    for(int i=1;i<=n;i++)
        T1.fa[i]=T2.fa[i]=i;
    for(int i=1;i<=n;i++){
        for(int v:g[i]){
            if(v>i) continue;
            v=T1.find(v);
            T1.fa[v]=i,T1.Fa[v]=i;
        }
    }
    for(int i=n;i;i--){
        for(int v:g[i]){
            if(v<i) continue;
            v=T2.find(v);
            T2.fa[v]=i,T2.Fa[v]=i;
        }
    }
    T1.work(),T2.work();
    for(int i=1;i<=n;i++){
        q[++m]=(Que){T1.in[i],T2.in[i],-1};
        q[++m]=(Que){T1.out[i],T2.in[i],1};
        s[i]=(Seg){T1.in[i],T2.in[i],T2.out[i]};
    }
    sort(q+1,q+1+m),sort(s+1,s+1+n);
    ll ans=0;
    for(int i=1,hd=1;i<=m;i++){
        while(hd<=n&&s[hd].pos<=q[i].pos)
            add(s[hd].l,1),add(s[hd].r+1,-1),hd++;
        ans+=query(q[i].x)*q[i].op;
    }
    printf("%d",ans);
}

标签:重构,return,int,超级,加倍,fa,机房,inline,include
来源: https://www.cnblogs.com/wwlwQWQ/p/15437089.html