牛客 NOIp模拟1 T3 保护 解题报告

保护

题目描述

\(C\)国有\(n\)个城市,城市间通过一个树形结构形成一个连通图。城市编号为\(1\)\(n\),其中\(1\)号城市为首都。国家有\(m\)支军队,分别守卫一条路径的城市。具体来说,对于军队\(i\),他守卫的城市区域可以由一对二元组\((x_i,y_i)\)代表。表示对于所有在\(x_i\)\(y_i\)的最短路径上的城市,军队\(i\)都会守卫他们。
现在有\(q\)个重要人物。对于一个重要人物\(j\),他要从他的辖区\(v_j\)出发,去到首都。出于某些原因,他希望找到一个离首都最近的,且在\(v_j\)到首都路径上的城市\(u_j\),使得至少有\(k_j\)支军队,能够全程保护他从\(v_j\)\(u_j\)上所经过的所有城市。换句话说,至少有\(k_i\)支军队,满足在树上,\(x_i\)\(y_i\)的路径能完全覆盖掉\(v_j\)\(u_j\)的路径。

输入描述:

第一行输入两个数\(n\),\(m\)
接下来\(n-1\)行,每行两个整数\(u\),\(v\),表示存在一条从城市\(u\)到城市\(v\)的道路。
接下来\(m\)行,每行两个整数\(x\),\(y\)。描述一个军队的守卫区域。
接下来一行一个整数\(q\)
接下来\(q\)行,每行两个整数\(v_j,k_j\)

输出描述:

对于每次询问,输出从\(v_j\)\(u_j\)最少需要经过多少条边。假如不存在这样的\(u_j\),则输出\(0\)

备注:

20%: n,m,q <= 300
40%: n,m,q <= 2000
60%: n,m,q <= 50000
100%: n,m,q <= 200000

覆盖必须是完全覆盖,不可以每个军队保护不同的段


设军队有为三元组\((x,y,z)\)

其中,\(x,y\)为军队的链端点,\(z\)为它们的\(LCA\)的深度

可以知道,每次询问\((v,k)\)都是查询满足\(dfn_v \le x \le low_v\)\(dfn_v \le y \le low_v\)的第\(k\)\(z\)

\(dfn_v\)~\(low_v\)是整棵子树的dfs序

可以直接CDQ套主席树求解

然而也可以用神奇的启发式合并

对每个节点建一颗权值线段树,代表这个节点的子树所包含的\(x\)\(y\)\(z\)值集合

注意这里并不需要容斥,因为如果\(x,y\)在同一颗子树那它们一定无法对这个子树根做出贡献

具体的,我们可以建出每个节点自己的\(z\)值集合,然后dfs合并上去

我这里直接用了子树的节点,所以会破坏子树的结构,于是先读进来然后离线做了

在线也可以,不过要多开一些节点


Code:

#include <cstdio>
#include <vector>
using namespace std;
const int N=2e5+10;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
    to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int f[N][19],dep[N],n,m,q;
void dfs_(int now)
{
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(v!=f[now][0])
        {
            f[v][0]=now;
            dep[v]=dep[now]+1;
            for(int j=1;j<=18;j++)
                f[v][j]=f[f[v][j-1]][j-1];
            dfs_(v);
        }
    }
}
vector <int > d[N];
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=18;~i;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=18;~i;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int sum[N*30],ch[N*30][2],tot,root[N];
#define ls ch[now][0]
#define rs ch[now][1]
void change(int &now,int l,int r,int pos)
{
    if(!now) now=++tot;
    if(l==r) {++sum[now];return;}
    int mid=l+r>>1;
    if(pos<=mid) change(ls,l,mid,pos);
    else change(rs,mid+1,r,pos);
    sum[now]=sum[ls]+sum[rs];
}
#define ols ch[las][0]
#define ors ch[las][1]
void Merge(int &now,int las,int l,int r)
{
    if(!now||!las) {now=las+now;return;}
    if(l==r) {sum[now]+=sum[las];return;}
    int mid=l+r>>1;
    Merge(ls,ols,l,mid);
    Merge(rs,ors,mid+1,r);
    sum[now]=sum[ls]+sum[rs];
}
struct node
{
    int i,k;
    node(){}
    node(int i,int k){this->i=i,this->k=k;}
}tt;
vector <node > ask[N];int ans[N];
int query(int now,int l,int r,int k)
{
    if(l==r) return l;
    int mid=l+r>>1;
    if(sum[ls]>=k) return query(ls,l,mid,k);
    else return query(rs,mid+1,r,k-sum[ls]);
}
void dfs(int now)
{
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(f[now][0]!=v)
        {
            dfs(v);
            Merge(root[now],root[v],1,n);
        }
    }
    for(int k,i=0;i<ask[now].size();i++)
    {
        k=ask[now][i].k;
        ans[ask[now][i].i]=max(0,dep[now]-query(root[now],1,n,k));
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int u,v,i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dep[1]=1;
    dfs_(1);
    for(int u,v,i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        int lca=LCA(u,v);
        change(root[u],1,n,dep[lca]);
        change(root[v],1,n,dep[lca]);
    }
    scanf("%d",&q);
    for(int v,k,i=1;i<=q;i++)
    {
        scanf("%d%d",&v,&k);
        tt=node(i,k);
        ask[v].push_back(tt);
    }
    dfs(1);
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}

2018.9.9

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9614371.html