牛客 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套主席树求解
upd:不需要套CDQ,借助差分的思想,主席树就可以解决 有一道基本一样的题可以看看[CQOI2015]任务查询系统
然而也可以用神奇的启发式合并
对每个节点建一颗权值线段树,代表这个节点的子树所包含的\(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