https://codeforces.com/gym/103941
题意
给定 n 个节点的树,每个节点编号为 1, 2, 3, …, n,第 i 个点的权值为
v
i
v_i
vi,
v
1
,
v
2
,
.
.
.
,
v
n
v_1, v_2, . . . , v_n
v1,v2,...,vn 是
0
,
1
,
.
.
.
,
n
−
1
0, 1, . . . , n−1
0,1,...,n−1 的一个排列。
对于 k = 0, 1, 2,…, n,找到最大的连通块使得其中所有点权值的 MEX 恰好为 k,输出最大满足的连通块大小。
1 ≤ n ≤ 1 0 6 1 ≤ n ≤ 10^6 1≤n≤106
思路
连通块中所有点的权值的 MEX 恰好为 k,也就是说连通块中至少要出现权值为 1~k-1 的 k-1 个点,并且权值为 k 的节点不在连通块中。
然后注意到这是一棵树,如果说权值为 k 的节点 x 不在连通块中,那么节点 x 所在的子树将会和整个连通块分开,所以:
需要注意的是,k = 0 时不需要满足所有小于 k 的节点都在连通块中,所以取该节点上部和下部连通块的最大值作为答案。
#include
using namespace std;
const int N = 1000010;
int n, m, a[N];
int dep[N], f[N][30], k;
int mina[N], mp[N], cnt[N];
vector<int> e[N];
void dfs(int x, int fa)
{
mina[x] = a[x];
cnt[x] = 1;
for(int tx : e[x])
{
if(tx == fa) continue;
dep[tx] = dep[x] + 1;
f[tx][0] = x;
for(int i=1;i<=k;i++)
f[tx][i] = f[f[tx][i-1]][i-1];
dfs(tx, x);
mina[x] = min(mina[x], mina[tx]);
cnt[x] += cnt[tx];
}
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
for(int i=k;i>=0;i--)
if(dep[f[x][i]] >= dep[y]) x = f[x][i];
if(x == y) return x;
for(int i=k;i>=0;i--)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int main(){
scanf("%d", &n);
k = log(n)/log(2);
for(int i=1;i<=n;i++) scanf("%d", &a[i]), mp[a[i]] = i;
for(int i=2;i<=n;i++)
{
int x; scanf("%d", &x);
e[x].push_back(i);
e[i].push_back(x);
}
dep[1] = 1;
dfs(1, 0);
int Lca = 0;
for(int i=0;i<=n;i++)
{
int x = mp[i];
if(i == 0)
{
int maxa = n - cnt[x];
for(int tx : e[x])
{
if(tx == f[x][0]) continue;
maxa = max(maxa, cnt[tx]);
}
cout << maxa << ' ';
}
else if(mina[x] >= a[x]){
cout << n - cnt[x] << ' ';
}
else if(dep[Lca] <= dep[x]) cout << 0 << ' ';
else
{
for(int tx : e[x])
{
if(tx == f[x][0]) continue;
if(mina[tx] == 0){
cout << cnt[tx] << ' ';
break;
}
}
}
if(!Lca) Lca = x;
else Lca = lca(Lca, x);
}
return 0;
}
场上做出来了,很激动~