[ 杂题 复杂度分析 ] Codeforces955F Heaps

对于 k=1 k = 1 ,可以直接树形DP求出答案。
对于 k>1 k > 1 ,显然 maxdepth<logkn m a x d e p t h < log k ⁡ n 。设 dpi,j d p i , j 表示 i i 号点,depth j j 时最大的 k ,那么状态数是 O(nlogn) O ( n log ⁡ n ) 的。
如果我们求出了 dpi,j d p i , j ,由于随着 k k 的减小,每个点的 depth 肯定是单调不降的,所以可以把每个 k k 要更新哪些点记下来,从大到小枚举 k ,更新相关点的 depth d e p t h 。由于一个点最多只会被更新 maxdepth m a x d e p t h 次,这部分的复杂度为 O(nlogn) O ( n log ⁡ n )
然后考虑怎么求 dpx,i d p x , i 。将所有儿子按照 dpv,i1 d p v , i − 1 从大到小排序,如果第 k k 大的值 k dpx,i d p x , i 就可以取到 k k 。从大到小枚举 k ,找到就退出。复杂度为 nni=2login=O(nlogn) n ∑ i = 2 n log i ⁡ n = O ( n log ⁡ n )

#include<bits/stdc++.h>
using namespace std;
char nc() {
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
void Read(int& x) {
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
#define fi first
#define se second
const int N=300010;
const int M=19;
typedef pair<int,int> abcd;
typedef long long ll;
int n,m,x,y;
int t[N<<1],nx[N<<1],h[N],num;
int f[N],c[N],dp[N][M];
int a[N],l,cur[N];
vector<abcd> g[N];
ll Res,Ans;
void Add(int x,int y) {
    t[++num]=y;nx[num]=h[x];h[x]=num;
}
bool Cmp(int x,int y) {
    return x>y;
}
void Dfs(int x,int y) {
    dp[x][1]=n;f[x]=y;
    for(int i=h[x];i;i=nx[i])
        if(t[i]!=y) Dfs(t[i],x),c[x]=max(c[x],c[t[i]]);
    for(int j=2;j<M;j++) {
        l=0;
        for(int i=h[x];i;i=nx[i])
            if(t[i]!=y&&dp[t[i]][j-1]) a[++l]=dp[t[i]][j-1];
        sort(a+1,a+l+1,Cmp);
        for(int k=l;k>1;k--)
            if(a[k]>=k) {
                dp[x][j]=k;
                break;
            }
        if(dp[x][j]) g[dp[x][j]].push_back(abcd(x,j));
    }
    Ans+=++c[x];
}
void Update(int x,int y) {
    while(x) {
        if(cur[x]>=y) break;
        Res+=y-cur[x];
        cur[x]=y;
        x=f[x];
    }
}
int main() {
    Read(n);
    for(int i=1;i<n;i++) Read(x),Read(y),Add(x,y),Add(y,x);
    Dfs(1,0);
    for(int i=1;i<=n;i++) cur[i]=1;
    Res=n;
    for(int k=n;k>1;k--) {
        for(int j=0;j<g[k].size();j++) Update(g[k][j].fi,g[k][j].se);
        Ans+=Res;
    }
    cout<<Ans<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值