国庆写一篇有国庆勋章,所以
我当前不是为勋章(确信)
题目链接:Attachments - 2022 CCPC Henan Provincial Collegiate Programming Contest - Codeforces
简略思路:
0连接1,再连接2,连接3....,,这个会形成一个联通体,如果当前判断的数在这个联通体中,答案为0,否则的话,联通体在当前判断的数的某一支树(比如叶子只有一个支树,联通体一定在该支树,其他结点有多个支树,找出联通体所在的)中所有的结点数就是答案
案例:
比如求k=4时的答案,那么0,1,2,3都将连接起来,那么从0开始,按唯一路径连接1,2,3,那么1,0,3,5,2都将被标记(如下图)
直接判断4是否被标记,如果被标记的话(也就是0,1,2,3进行连接的时候,4一定也被连接),答案就是0
如果4没有被标记(如下图),那么点4有3条支树(分别是4向5的,4向7的,4向8的),判断连接的联通体在哪个支树,是4向5的支树,其所有结点数是5,3,0,1,6,2。答案是6个
也就是
1、从0开始连接1,2,3...,
2、判断是否被连接其中,没有的话判断在该点的哪个分支,分支所有数量就是答案
很麻烦,但是0为根的话,就很简单,很简单的树形DP了
容我娓娓道来:
1、因为0开始,连接每个点只需不停地往上走,直到连接这个连通块(也就是遇到标记的停)
2、判断就是看下标vis[i]==1?,如果没有被标记,连通块一定在这个点的上面(因为0一定在上面),答案就是n-(该点下面的结点数)
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
#define M 1000005
int n,x,y,ans=0;
int a[M],fa[M];
int dp[M],vis[M]; //dp就是包括该节点下面所有的结点数
vector<int>v[M];
void dfs(int d,int pre){
dp[d]=1;
fa[d]=pre;
int l=v[d].size();
for(int i=0;i<l;i++){
int now=v[d][i];
if(pre!=now){
dfs(now,d);
dp[d]+=dp[now];
if(d==0){
ans=max(ans,dp[now]);
}
}
}
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<=n;i++){
cin>>x;
x=a[x];
y=a[i];
v[x].push_back(y); //压入的就是结点的值,下面遍历就是从0开始遍历
v[y].push_back(x);
}
dfs(0,-1);
vis[0]=1;
cout<<ans<<" "; //0的答案特判
for(int i=1;i<=n;i++){
if(vis[i]){ //如果在联通块中,答案为0
cout<<0<<" ";
}else{
cout<<n-dp[i]<<" "; //否则答案是n-下面结点数,因为0是根,连通块一定在上面
}
x=i;
while(vis[x]!=1){ //指定结点往上走,直到已经标记过的结束
vis[x]=1;
x=fa[x];
}
}
return 0;
}