peiwenjun's blog 没有知识的荒原

P7880 [Ynoi2006] rldcot 题解

题目描述

给定一棵 \(n\) 个节点的树, \(1\) 号点为根,边有边权 \(w_i\)

\(m\) 次询问,给定 \(l,r\) ,对所有满足 \(l\le i\le j\le r\) 的点对 \((i,j)\) ,求本质不同的 \(dep(lca(i,j))\) 个数。

数据范围

  • \(1\le n\le 10^5,1\le m\le 5\cdot 10^5,-10^9\le d\le 10^9,1\le l\le r\le n\)

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{512MB}\)

分析

容易发现 \(dep\) 并没有什么用,因此本题等价于区间虚树数颜色

显然我们没办法维护虚树的形态,只能维护每个点是否在虚树上出现。

定义三元组 \((l,r,x)\) 表示,若询问区间包含 \([l,r]\) ,那么 \(x\) 可以产生贡献。

那么一个 \(x\) 会对应若干个三元组 \((l,r,x)\)\(x\) 产生贡献当且仅当询问区间包含任意一个 \([l,r]\)

我们希望这样的刻画方式是充要的,有一个保证充要但是显然会超时的方法:

对每个节点 \(x\) ,将 \(x\) 子树中所有点放到数轴上,对于相邻且不在同一子树的两点 \((l,r)\) ,构建三元组 \((l,r,x)\)

构建三元组的代价为 \(\mathcal O(n^2)\) ,但是注意到三元组个数不超过轻子树大小总和,所以三元组个数只有 \(\mathcal O(n\log n)\)

接下来目标是快速找到每一个三元组,考虑启发式合并

set 维护重子树,每次用轻子树的所有点在 set 中查询前驱后继并构建三元组,最后执行合并即可。

接下来的问题和整棵树就没啥关系了:

\(\mathcal O(n\log n)\) 个三元组 \((l,r,x)\) ,若 \([l,r]\subseteq\) 询问区间,则颜色 \(x\) 被激活,求被激活的不同颜色数。

本质是矩形数颜色,考虑扫描线:

扫描右端点 \(r\) ,显然每个 \(x\) 的贡献是一段前缀,维护能让 \(x\) 产生贡献的最大左端点 \(l\) ,树状数组支持单点修改后缀求和即可。

时间复杂度 \(\mathcal O(n\log^2n+m\log n)\)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=1e5+5;
int l,m,n,r,u,v,w,cnt;
ll c[maxn],d[maxn];
int id[maxn],sz[maxn],dfn[maxn],son[maxn];
int pos[maxn],res[5*maxn];
set<int> s;
vector<pii> g[maxn],h[maxn],q[maxn];
void dfs1(int u,int fa)
{
    sz[u]=1;
    for(auto p:g[u])
    {
        int v=p.fi,w=p.se;
        if(v==fa) continue;
        d[v]=d[u]+w,dfs1(v,u),sz[u]+=sz[v];
        if(sz[v]>=sz[son[u]]) son[u]=v;
    }
}
void push(int l,int r,int x)
{
    h[r].push_back(mp(l,d[x]));
}
void dfs2(int u,int fa,int op)
{
    dfn[u]=++cnt,id[cnt]=u,push(u,u,u);
    for(auto p:g[u])
    {
        int v=p.fi;
        if(v!=fa&&v!=son[u]) dfs2(v,u,0);
    }
    if(son[u]) dfs2(son[u],u,1);
    for(auto p:g[u])
    {
        int v=p.fi;
        if(v==fa||v==son[u]) continue;
        for(int i=dfn[v];i<dfn[v]+sz[v];i++)
        {
            int x=id[i];
            auto it=s.lower_bound(x);
            if(it!=s.end()) push(x,*it,u);
            if(it!=s.begin()) push(*--it,x,u);
        }
        for(int i=dfn[v];i<dfn[v]+sz[v];i++) s.insert(id[i]);
    }
    if(!op) s.clear();
    else s.insert(u);
}
void add(int x,int v)
{
    while(x) c[x]+=v,x-=x&(-x); 
}
int query(int x)
{
    int res=0;
    while(x<=n) res+=c[x],x+=x&(-x);
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        g[u].push_back(mp(v,w)),g[v].push_back(mp(u,w));
    }
    dfs1(1,0);
    for(int i=1;i<=n;i++) c[i]=d[i];
    sort(c+1,c+n+1);
    int k=unique(c+1,c+n+1)-c-1;
    for(int i=1;i<=n;i++) d[i]=lower_bound(c+1,c+k+1,d[i])-c;
    memset(c,0,sizeof(c));
    dfs2(1,0,1);
    for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),q[r].push_back(mp(l,i));
    for(int i=1;i<=n;i++)
    {
        for(auto p:h[i]) if(p.fi>=pos[p.se]) add(pos[p.se],-1),add(p.fi,1),pos[p.se]=p.fi;
        for(auto p:q[i]) res[p.se]=query(p.fi);
    }
    for(int i=1;i<=m;i++) printf("%d\n",res[i]);
    return 0;
}

posted on 2023-03-28 21:09  peiwenjun  阅读(12)  评论(0)    收藏  举报

导航