D. MEX Tree(容斥+双指针)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D. MEX Tree(容斥+双指针)相关的知识,希望对你有一定的参考价值。
D. MEX Tree(容斥+双指针)
令 a n s i ans_i ansi为异或值为 i i i的路径数。
令 f i f_i fi为异或值 ≥ i \\ge i ≥i的路径数。
令 g i g_i gi为异或值 > i >i >i的路径数。
令 s z i sz_i szi表示结点 i i i的子树大小。
容易发现: g i = f i + 1 g_i=f_{i+1} gi=fi+1
考虑用容斥计算 a n s i = f i − g i ans_i=f_i-g_i ansi=fi−gi
如果我们能计算出 g i , i ∈ [ 1 , n ] g_i,i\\in [1,n] gi,i∈[1,n] 我们就可以递推得到 a n s i ans_i ansi。
f 0 f_0 f0 就是整棵树的路径数, a n s 0 ans_0 ans0 所有 0 0 0的儿子子树内的路径数之和。
f 0 = n ( n − 1 ) 2 , a n s 0 = ∑ v ∈ s o n 0 s z v ( s z v − 1 ) 2 \\large f_0=\\dfrac{n(n-1)}{2},ans_0=\\sum\\limits_{v\\in son_0}\\dfrac{sz_v(sz_v-1)}{2} f0=2n(n−1),ans0=v∈son0∑2szv(szv−1)
g 0 = f 0 − a n s 0 = f 1 g_0=f_0-ans_0=f_1 g0=f0−ans0=f1
所以接下来的关键就是求 g i , ∈ [ 1 , n ] g_i, \\in [1,n] gi,∈[1,n]
考虑用双指针。
用两个指针 l , r l,r l,r 分别表示 g i g_i gi 对应的链的端点。
则答案就是: s z l × s z r sz_l\\times sz_r szl×szr
显然这条链包含 0 , 1 , 2 , … , i 0,1,2,\\dots ,i 0,1,2,…,i 结点,且 i = l i=l i=l或 i = r i=r i=r。
我们考虑这条链如何扩展到 g i g_{i} gi。
显然要满足 g i g_{i} gi必须满足 g i − 1 g_{i-1} gi−1,所以 g i g_i gi 必须在 p a t h ( l , r ) path(l,r) path(l,r)这条路径上扩展,也就是说 i i i 往上走必须走到 l l l或者 r r r。
如果 i i i走步到 l l l或者 r r r,说明无解 g i = 0 g_{i}=0 gi=0,则 a n s j = 0 , ( j > i ) ans_j=0,(j\\ >i) ansj=0,(j >i)直接 b r e a k break break。
否则更新 p a t h ( l , r ) path(l,r) path(l,r)。
需要注意的是如果 l = 0 l=0 l=0或者 r = 0 r=0 r=0,计算贡献时要 s z 0 sz_0 sz0减掉对应的那颗子树,注意是永久性修改,因为链是继承关系。
时间复杂度: O ( n ) O(n) O(n)
#include<bits/stdc++.h>
#define ll long long
const int N = 2e5+10;
int t,n,fa[N],cov[N];
ll sz[N],ans[N],fl,p,q;
std::vector<int> g[N];
void dfs(int x,int f){
fa[x] = f ;
sz[x] = 1 ;
for(auto p:g[x]){
if(p==f) continue;
dfs(p,x);
sz[x]+=sz[p];
}
}
void add(int x){
if(cov[x]) return ;
if(!cov[fa[x]]) add(fa[x]);
cov[x] = 1;
if(fa[x]!=q&&fa[x]!=p){
fl = 0 ;
return ;
}
else{
if(fa[x]==p) p=x;
else q=x;
if(fa[x]==0) sz[0]-=sz[x];
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i<=n;++i) g[i].clear(),ans[i]=0,cov[i]=0;
for(int i=1;i<=n-1;++i){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v]以上是关于D. MEX Tree(容斥+双指针)的主要内容,如果未能解决你的问题,请参考以下文章