CF293E Close Vertices

CF

洛谷

码农但是好懂的方法。

  • 给出 \(n\) 个点的树。边有长度为 \(1\),以及权值为 \(w\)。求有多少对无序点对 \((i,j)(i\ne j)\) 满足 \(i,j\) 路径上边长度和不超过 \(L\),以及权值和不超过 \(W\)

  • \(n\le 10^5\)\(W\le 10^9\)

看到树上路径问题,貌似没法 dp,于是考虑点分治。如果去掉 \(L\) 的限制,就是 P4178。于是考虑把 P4178 的树状数组换成树套树。然后你会发现这题卡了 \(\mathcal{O}(n\log^3 n)\),喜提 TLE。

怎么办呢?设当前根为 \(rt\),点 \(u\)\(rt\) 的边长和为 \(len_u\),边权和为 \(val_u\)

先计算子树内的点之间的贡献,考虑怎样的点 \(v\) 能够给点 \(u\) 贡献,显然 \(len_v\le L-len_u\)\(val_v\le W-val_u\)。典型二维偏序。观察到 \(W\) 值域较大,于是将所有点按照 \(val\) 从小到大排序,并按照这个顺序建立主席树。主席树的节点内 \([l,r]\) 维护当前版本 \(len_v\) 在这个区间内的数的个数。查询的时候,先二分出 \(val_v\le W-val_u\) 的最后一个版本,然后查询区间 \([1,L-len_u]\) 即可。注意到自己可能会统计到自己,如果 \(2\times len_u\le W\)\(2\times val_u\le W\),则将答案减去 \(1\)

由于一条路径会在 \(u,v\) 两点处计算到(移项一下 \(u\) 也满足 \(v\) 的条件),将答案再除以二。

但是仍然会统计到同一子树之间的贡献,于是在 dfs 子树的时候,将子树内的节点存起来,然后它们进行上述的计算。再将这些结果减去。

再考虑子树到根的贡献,对于一个点,只要满足 \(len_u\le L\)\(val_u\le W\)。主席树上类似查询即可。

时间复杂度为 \(\mathcal{O}(n\log^2n)\),空间复杂度为 \(\mathcal{O}(n\log n)\)

记录

$\color{orange}\bf 代码$
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define lb(x) ((x)&(-x))
#define ll long long
using namespace std;
const int N=1e5+5;
int n,L,W,rt,sz[N],mx[N],tot,len[N],val[N],cnt,ls[N*30],rs[N*30],ver[N],node,a[N],num;
vector<pair<int,ll>>g[N];
pair<int,int>p[N],q[N];
ll ans,sum[N*30];
bool vis[N];
void insert(int&x,int y,int l,int r,int k){
    x=++node;
    sum[x]=sum[y]+1;
    if(l^r){
        int m=(l+r)>>1;
        if(k<=m){
            rs[x]=rs[y];
            insert(ls[x],ls[y],l,m,k);
        }else{
            ls[x]=ls[y];
            insert(rs[x],rs[y],m+1,r,k);
        }
    }
}
ll query(int x,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){
        return sum[x];
    }
    int m=(l+r)>>1;
    ll ret=0;
    if(ql<=m&&ls[x]){
        ret+=query(ls[x],l,m,ql,qr);
    }
    if(qr>m&&rs[x]){
        ret+=query(rs[x],m+1,r,ql,qr);
    }
    return ret;
}
void gravity(int u,int fa){
    sz[u]=1;
    mx[u]=0;
    for(auto[v,w]:g[u]){
        if((v^fa)&&!vis[v]){
            gravity(v,u);
            sz[u]+=sz[v];
            mx[u]=max(mx[u],sz[v]);
        }
    }
    mx[u]=max(tot-sz[u],mx[u]);
    if(mx[u]<mx[rt]){
        rt=u;
    }
}
void get(int u,int fa){
    q[++num]=p[++cnt]={val[u],len[u]};
    for(auto[v,w]:g[u]){
        if((v^fa)&&!vis[v]){
            len[v]=len[u]+1;
            val[v]=val[u]+w;
            get(v,u);
        }
    }
}
ll cal(pair<int,int>*p,int cnt){
    stable_sort(p+1,p+1+cnt);
    for(int i=1;i<=cnt;++i){
        insert(ver[i],ver[i-1],1,n,p[i].second);
    }
    ll s=0;
    int k=cnt;
    for(int i=1;i<=cnt;++i){
        if(p[i].first>W){
            k=i-1;
            break;
        }
        if(p[i].second>=L){
            continue;
        }
        int l=1,r=cnt,f=0;
        while(l<=r){
            int m=(l+r)>>1;
            if(p[m].first<=W-p[i].first){
                f=m;
                l=m+1;
            }else{
                r=m-1;
            }
        }
        s+=query(ver[f],1,n,1,L-p[i].second);
        if((p[i].first<<1)<=W&&(p[i].second<<1)<=L){
            --s;
        }
    }
    return s>>1;
}
void clear(int&tmp){
    for(int i=1;i<=node;++i){
        sum[i]=ls[i]=rs[i]=0;
    }
    for(int i=1;i<=tmp;++i){
        ver[i]=0;
    }
    tmp=node=0;
}
void divide(int u){
    vis[u]=1;
    for(auto[v,w]:g[u]){
        if(!vis[v]){
            len[v]=1;
            val[v]=w;
            get(v,u);
            ans-=cal(q,num);
            clear(num);
        }
    }
    if(!cnt){
        return;
    }
    ans+=cal(p,cnt);
    int l=1,r=cnt,f=0;
    while(l<=r){
        int m=(l+r)>>1;
        if(p[m].first<=W){
            f=m;
            l=m+1;
        }else{
            r=m-1;
        }
    }
    ans+=query(ver[f],1,n,1,L);
    clear(cnt);
    for(auto[v,w]:g[u]){
        if(!vis[v]){
            tot=sz[v];
            mx[rt=0]=1e9;
            gravity(v,u);
            gravity(rt,0);
            divide(rt);
        }
    }
}
int main(){
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n>>L>>W;
    for(int i=1,p,w;i^n;++i){
        cin>>p>>w;
        g[i+1].emplace_back(p,w);
        g[p].emplace_back(i+1,w);
    }
    tot=n;
    mx[0]=1e9;
    gravity(1,0);
    gravity(rt,0);
    divide(rt);
    cout<<ans;
}
posted @ 2023-07-06 11:02  lzyqwq  阅读(19)  评论(0)    收藏  举报