[CF1039D]You Are Given a Tree

Description:

a.png

Solution:

\(O(n^2)\) 的做法就是每次 dfs 整棵树 贪心从下往上选,具体而言就是维护以 u 儿子传上来的最长链和次长链,如果最长链 + 次长链 + 1 \(\geq\) \(k\) 则拼成一条长度为 \(k\) 的链,然后穿一条长度为 0 的上去,否则把最长链传上去。

正确性:由于当前存在最长链 + 次长链 +1\(\geq\) \(k\),就算传一条最长链上去也只可能使答案增加1,而不如现在就让最长链匹配了次长链产生1的贡献,可能会更优。

发现当\(k\geq \sqrt n\)时,答案 \(\le \sqrt n\),而且不难发现答案随着k变大变小,并且由一些连续段拼接而成,我们对于每个答案二分其最右边的端点使得这一块的答案一样,所以总时间复杂度为 \(O(n\sqrt n\log n)\)

这题比较卡常,不能写递归的 dfs,把每个点的 dfn 序搞出来就可以用 for 循环实现了。

Code:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

using namespace std;

#define LL long long
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define REP(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i))
#define GO cerr << "GO" << endl;

inline void proc_status()
{
    ifstream t("/proc/self/status");
    cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
template<class T> inline T read() 
{
    register char c;
    register T x(0), f(1);
    while (!isdigit(c = getchar())) if (c == '-') f = -1;
    while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
    return x * f;
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = (int) 1e5;

int n;
int ans[maxN + 2],ver[maxN<<1],nxt[maxN<<1],head[maxN+2],tot,dfst,rev[maxN+2];
int first[maxN+2],second[maxN+2],fa[maxN+2];

void link(int u, int v)
{ ver[++tot]=v,nxt[tot]=head[u],head[u]=tot; }

void Input()
{
    n = read<int>();
    for (int i = 1; i < n; ++i)
    {
        int u = read<int>(), v = read<int>();
        link(u, v), link(v, u);
    }
}

void dfs(int u, int f)
{
    rev[++dfst]=u;
    fa[u]=f;
    for (int i = head[u];i;i=nxt[i])
    {
        int v=ver[i];
        if(v==f)continue;
        dfs(v,u);
    }
}

inline int Calc(int l)
{
    memset(first,0,4*(n+1));
    memset(second,0,4*(n+1));
    int res(0);
    for (register int i = n; i >= 1; --i)
    {
        int u=rev[i],f=fa[u],len;
        if(first[u]+second[u]+1>=l) ++res,len=0;
        else len=first[u]+1;
        if (!f) continue;
        if(first[f] < len)second[f]=first[f],first[f]=len;
        else if(second[f]<len)second[f]=len;
    }
    return res;
}

void Solve()
{
    int i;
    int sqr = int(sqrt(n));

    dfs(1,0);
    for (i = 1; i <= sqr; ++i) ans[i] = Calc(i);
    for (; i <= n; ++i)
    {
        ans[i]=Calc(i);
        int l=i,r=n,pos=i,mid;
        while (l<=r)
        {
            mid=(l+r)>>1;
            if(Calc(mid)==ans[i])pos=mid,l=mid+1;
            else r=mid-1;
        }
        for(int j=i+1;j<=pos;++j)ans[j]=ans[i];
        i=pos;
    }
    for(int i=1;i<=n;++i)
        printf("%d\n",ans[i]);
}

int main() 
{ 
    Input();
    Solve();
    return 0;
}

转载于:https://www.cnblogs.com/cnyali-Tea/p/11553724.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值