Codechef Starters 133 Fireworks 题解

题意

题目链接

使用 coze.com 的 gpt-4 进行翻译和格式美化.

给定一个包含$N$个顶点的树$T$,设$F$是$T$的顶点的一个子集,如果$F$满足以下条件,则称$F$为$x$根火花:

  1. $F$包含顶点$x$ – $F$是$T$的连通子图——也就是说,由$F$导出的子图$T[F]$是连通的。
  2. 令$d_F(u)$表示节点$u$在$T[F]$中的度。然后,对于所有$y \in F$,并且$y \neq x$,$d_F(y) \leq 2$应成立。也就是说,在$F$中,不是$x$的每个顶点最多应该有两个邻居。
  3. 令$d_T(u)$表示节点$u$在$T$图中的度。然后,对于所有$y \in F$并且$y \neq x$,$d_T(y) \neq d_T(x)$应成立。

图$G$中节点的度是与其在$G$中的边的数量。

对于从$1$到$N$的每个$x$,找到$x$根火花的最大大小。

解析

一个很有趣的题目。

不妨先考虑对于一个点 $u$ 怎么统计答案。首先我们发现由于最终图除 $u$ 外的所有点的度数不多于 $2$,所以最大火花显然是一个以 $u$ 为中心的星形图。记点 $u$ 的度数为 $d_u$,我们可以删除树中所有度为 $d_u$ 的点,然后原树被分割成了一个由若干树组成的森林。对于与 $u$ 相连的每一个点,我们计算这个点在分割后其所在的树上能到达的最远距离,记这条最长路上点的数量为 $dis_v$。则 $ans_u=1+ \sum dis_v$​。

既然我们删除了所有度为 $d_u$ 的点,我们不妨统计所有度为 $d_u$ 点的答案。即,我们枚举度数 $k$,删除所有度数为 $k$ 的点。然后计算出所有度不为 $k$ 的点在其分割后所在的树上的 $dis_u$,接着我们再用上段的方法对所有度数为 $k$ 的点统计答案即可。

根据 DP on Trees – Solving For All Roots 提供的 dp 方法,或者是求树的直径的方法。我们可以在 $O(n)$ 计算所有点的 $dis_u$,同时我们也可以在 $O(n)$ 的时间内统计所有度数为 $k$​ 的点的答案(因为每条边最多被访问两次)。

然后我们发现复杂度是正确的。因为 $\sum_{i=1}^n =\frac{(1+n)n}{2}$,度数 $k$ 最多只有 $O(\sqrt n)$ 种可能的取值,故最终时间复杂度为 $O(n\sqrt n)$。

代码

#include<bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
using namespace std;
const int N=1e5+5;
const int M=2e5+5;
struct node{
    int to,nxt;
}e[M];
int head[N],cnt;
void add(int u,int v){
    e[++cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
int du[N],n,f[N],f2[N],g[N],ans[N];
void dfs(int u,int fa,int k){
    if(du[u]==k) return;
    f[u]=f2[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;if(v==fa) continue;
        dfs(v,u,k);
        if(f[v]+1>f2[u]) f2[u]=f[v]+1;
        if(f2[u]>f[u]) swap(f[u],f2[u]);
    }
}
void dfs2(int u,int fa,int k){
    if(du[u]==k) return;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;if(v==fa||du[v]==k) continue;
        if(f[v]+1==f[u]) g[v]=max(g[u]+1,f2[u]+1);
        else g[v]=max(g[u]+1,f[u]+1);
        dfs2(v,u,k);
    }
}
void furthersolve(int k){
    for(int i=1;i<=n;i++){f[i]=f2[i]=0;g[i]=1;}
    for(int i=1;i<=n;i++){
        if(du[i]==k||f[i]) continue;
        dfs(i,i,k);dfs2(i,i,k);
    }
    for(int u=1;u<=n;u++){
        if(du[u]!=k) continue;
        ans[u]=1;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;if(du[v]==k) continue;
            //printf("%d %d %d\n",u,v,max(f[v],g[v]));
            ans[u]+=max(f[v],g[v]);
        }
    }
}
void solve(){
    scanf("%d",&n);cnt=0;
    vector<int> vec[n+1];
    for(int i=1;i<=n;i++){
        head[i]=du[i]=0;    
    }
    for(int i=1,u,v;i<n;i++){
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
        du[u]++;du[v]++;
    }
    for(int i=1;i<=n;i++) vec[du[i]].pb(i);
    for(int i=1;i<=n;i++){
        if(vec[i].empty()) continue;
        furthersolve(i);
    }
    for(int i=1;i<=n;i++){
        printf("%d ",ans[i]);
    }
    printf("\n");
}
signed main(){
    int T;
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}
本文作者:water_tomato
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇