[DS记录]P7126 [Ynoi2008] rdCcot
command_block
2021-06-28 12:12:19
**题意** : 给一棵 $n$ 个点的树和常数 $C$。
每次查询给出区间 $[l,r]$,查询有多少个 C-块,定义如下:
对任意两个节点 $a,b$,定义 $a,b$ 是 C-连通的,当且仅当存在一个长为 $t$ 的节点序列 $\{v_i\}$,满足:
1. $v_1=a$
2. $v_t=b$
3. 对任意 $1\le i\le t-1$,$dis(v_i,v_{i+1})\le C$
4. 对任意 $1\le i\le t$,$l\le v_i\le r$
定义“C-块”为一个点集 $S$,满足:
1. 对任意 $a\in S$,$b$ 属于 $S$ 的补集,$a,b$ 不 C-连通
2. 对任意 $a,b\in S$,$a$ 和 $b$ C-连通
3. 对任意 $a\in S$,有 $l\le a \le r$
$n\leq 3\times 10^5,m\leq 6\times 10^5$ ,时限 $\texttt{2s}$。
------------
- ### **Part1**
不难发现 C-块 点集是不交的,且任意两个 C-块 的虚树也是不交的。
考虑在每个 C-块 的最浅点将其统计,若同样浅,则选择编号较小的。(注意不是虚树的最浅点)
我们按照这个规则定义点的大小关系。
先考虑静态问题。给出一个点集 $S$ 求 C-块个数。
每个点向距离 $C$ 以内的最小点连边(如果这个最小点比自己小的话)。可以发现,没有出边的点就是一个 C-块 的最小点。
- **正确性说明** : 相当于要证 : 从点 $u$ 不断跳向距离 $C$ 以内的最小点,最终可以来到所在 C-块 的最小点。
只有距离 $C$ 以内没有更小的点才不能跳,因此上面的结论是显然的。
- ### **Part2**
接下来考虑区间询问。
我们只需要快捷地判定某个点是否存在出边。
记距点 $i$ 在 $C$ 以内的,小于点 $i$ 的,编号离 $i$ 最接近的两个点分别为 $l_i,r_i$ ,一左一右。
对于点 $i\in [l,r]$ ,若 $l_i<l$ 且 $r<r_i$ ,则 $i$ 没有出边,是某个 C-块 中的最小点。
考虑求出 $l_i,r_i$ 之后如何回答询问。
使用扫描线,增大右端点并维护每个左端点的答案。
对子 $(l_i,r_i)$ 当 $r$ 增大到 $i$ 时,开始将左端点 $(l_i,i]$ 的答案均 $+1$。当 $r$ 增大到 $r_i$ 时,取消贡献。
树状数组即可。
- ### **Part3**
考虑如何求出 $l_i,r_i$。
使用点分治。对于点 $u$ ,记到分治中心的距离为 $dis_u$ 。
每个点 $v$ 会询问来自其他分支的(这个不用管,一定更劣), 比 $v$ 小的,满足 $dis_u+dis_v\leq C$ 的,编号最接近的 $u$。
考虑静态问题。将点按照大小排序,逐个插入以点编号为下标的线段树,线段树内维护 $dis$ 的最小值。
查询时,只能走存在 $dis_u\leq C-dis_v$ 的分支,在此基础上,尽量“向左”或“向右”走即可。
这容易用线段树维护。总复杂度 $O(n\log^2n+m\log n)$。
有点卡常,去堆栈 + 快读+ 剪枝才勉强卡过。
```cpp
#include<algorithm>
#include<cstdio>
#include<vector>
#define pb push_back
#define MaxN 300500
using namespace std;
namespace io {
char buf[1<<22],*p1=buf,*p2=buf,obuf[1<<22],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,(1<<22)-10,stdin),p1==p2)?EOF:*p1++)
int rd() {
int x=0;char ch=getchar();
while(ch<'0'||'9'<ch)ch=getchar();
while('0'<=ch&&ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
void putc(char ch){*O++=ch;}
void print(int x){if(x>9)print(x/10);*O++=x%10+'0';}
void flush(){fwrite(obuf,O-obuf,1,stdout);}
}
const int INF=1000000000;
struct Point{int l,r,p;}p[MaxN],p2[MaxN],b[MaxN<<1];
bool cmpP(const Point &A,const Point &B){return A.r<B.r;}
vector<int> g[MaxN];
int dep[MaxN];
bool cmpU(int A,int B)
{return dep[A]==dep[B] ? A<B : dep[A]<dep[B];}
void pfs2(int u)
{
for (int i=0,v;i<g[u].size();i++)
if (!dep[v=g[u][i]]){
dep[v]=dep[u]+1;
pfs2(v);
}
}
int e[MaxN],ef,siz[MaxN],mp[MaxN],st[MaxN],tn;
void pfs(int u)
{
e[st[++tn]=u]=ef;
siz[u]=1;mp[u]=0;
for (int i=0,v;i<g[u].size();i++)
if (e[v=g[u][i]]<ef){
pfs(v);
siz[u]+=siz[v];
mp[u]=max(mp[u],siz[v]);
}
}
int grt(int u)
{
int rt=tn=0;ef++;pfs(u);
for (int i=1;i<=tn;i++){
int u=st[i];
mp[u]=max(mp[u],tn-siz[u]);
if (mp[u]<mp[rt])rt=u;
}return rt;
}
int dis[MaxN];
void dfs(int u)
{
e[u]=ef;
for (int i=0,v;i<g[u].size();i++)
if (e[v=g[u][i]]<ef){
dis[v]=dis[u]+1;
dfs(v);
}
}
int n,a[MaxN<<2],tp[MaxN],lim;
void build(int l,int r,int u)
{
if (l==r){tp[l]=u;lim=max(lim,u);return ;}
int mid=(l+r)>>1;
build(l,mid,u<<1);build(mid+1,r,u<<1|1);
}
void add(int p,int c)
{for (int u=tp[p];u;u>>=1)a[u]=min(a[u],c);}
void clr(int u)
{
if (a[u]==INF)return ;
a[u]=INF;
if ((u<<1)>lim)return ;
clr(u<<1);clr(u<<1|1);
}
int wfl,wfr,wfc,buf;
int qry1(int l,int r,int u)
{
if (r<=buf)return buf;
if (l==r)return a[u]<=wfc&&l<=wfr ? l : 0;
int mid=(l+r)>>1;
if (wfr>mid&&a[u<<1|1]<=wfc){
int sav=qry1(mid+1,r,u<<1|1);
if (sav)return sav;
}return qry1(l,mid,u<<1);
}
int qry2(int l,int r,int u)
{
if (buf<=l)return buf;
if (l==r)return a[u]<=wfc&&wfl<=l ? l : n+1;
int mid=(l+r)>>1;
if (wfl<=mid&&a[u<<1]<=wfc){
int sav=qry2(l,mid,u<<1);
if (sav!=n+1)return sav;
}return qry2(mid+1,r,u<<1|1);
}
int C;
void solve(int u)
{
if (tn==1)return ;
ef++;dis[u]=0;dfs(u);e[u]=INF;
sort(st+1,st+tn+1,cmpU);
for (int i=1;i<=tn;i++){
int u=st[i];
if (dis[u]>C)continue;
add(u,dis[u]);
wfc=C-dis[u];
wfr=u-1;buf=p[u].l;p[u].l=max(p[u].l,qry1(1,n,1));
wfl=u+1;buf=p[u].r;p[u].r=min(p[u].r,qry2(1,n,1));
}clr(1);
for (int i=0,v;i<g[u].size();i++)
if (e[v=g[u][i]]!=INF)solve(grt(v));
}
#define lbit(p) (p&-p)
struct SumDS
{
int t[MaxN];
void add(int p,int c)
{while(p<=n+2){t[p]+=c;p+=lbit(p);}}
int qry(int p){
int ret=0;
while(p){ret+=t[p];p^=lbit(p);}
return ret;
}
}T;
int m,ans[MaxN<<1];
int main()
{
n=io::rd();m=io::rd();C=io::rd();
for (int i=2,fa;i<=n;i++){
fa=io::rd();
g[fa].pb(i);g[i].pb(fa);
}
dep[1]=1;pfs2(1);
for (int i=1;i<=n;i++){p[i].r=n+1;p[i].p=i;}
build(1,n,1);clr(1);
mp[0]=n;solve(grt(1));
for (int i=1;i<=m;i++)
{b[i].l=io::rd();b[i].r=io::rd();b[i].p=i;}
for (int i=1;i<=n;i++)p2[i]=p[i];
sort(p+1,p+n+1,cmpP);
sort(b+1,b+m+1,cmpP);
for (int i=1,j=1,k=1;i<=m;i++){
while(k<=b[i].r){
T.add(p2[k].l+1,1);
T.add(k+1,-1);
k++;
}
while(j<=n&&p[j].r<=b[i].r){
T.add(p[j].l+1,-1);
T.add(p[j].p+1,1);
j++;
}
ans[b[i].p]=T.qry(b[i].l);
}
for (int i=1;i<=m;i++)
{io::print(ans[i]);io::putc('\n');}
io::flush();
return 0;
}
```