• CF955F Heaps 题解


    Codeforces
    Luogu

    Description.

    给定一棵有根树,定义 \(F_k(i)\) 表示第 \(i\) 号点子树内最大的满 \(k\) 叉树,求

    \[\sum_{i=1}^n\sum_{j=1}^nF_j(i) \]

    Range.

    \(n\le 3\times 10^5\)

    Solution.

    首先考虑暴力 \(dp\),设 \(dp_{i,j}\) 表示 \(F_j(i)\)
    然后,我们考虑每次转移,\(dp_k\) 从它儿子中找到第 \(k\) 大,转移过来。
    看上去很难维护,复杂度只能做到 \(O(n^2\log n)\)(基排优化一个 \(\log\)

    然后,我们考虑观察性质,好像并没有什么性质。
    我们发现,如果 \(k\ne 1\),那么就必然有 \(dp_k(x)\le O(\log n)\)
    考虑经典套路,就是把 \(dp\) 的答案维和 \(dp\) 维互换,状态数变成了 \(n\log n\)
    状态相当于变成了 \(dp_{k}(x)\) 表示在第 \(x\) 个点,度数至多为多少时,它有 \(x\) 层。
    然后,我们转移的时候,要找到 \(dp_{k}(y)\ge cnt(y)\)\(cnt(y)\) 表示在 \(x\) 的孩子内,有几个比它小。
    然后直接暴力转移即可,复杂度 \(O(n\log ^2n)\)

    Coding.

    点击查看代码
    //是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
    #include<bits/stdc++.h>
    using namespace std;typedef long long ll;
    template<typename T>inline void read(T &x)
    {
    	x=0;char c=getchar(),f=0;
    	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
    	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    	f?x=-x:x;
    }/*}}}*/
    const int N=300005;struct edge{int to,nxt;}e[N<<1];int et,head[N];
    int n,ln[N],dp[N][20];ll rs=0;
    inline void adde(int x,int y) {e[++et]=(edge){y,head[x]},head[x]=et;}
    inline void dfs0(int x,int fa)
    {
    	ln[x]=1;for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
    		dfs0(e[i].to,x),ln[x]=max(ln[e[i].to]+1,ln[x]);
    	vector<int>v;dp[x][1]=n,rs+=ln[x];for(int k=2;k<20;k++)
    	{
    		v.clear();for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
    			v.push_back(dp[e[i].to][k-1]);
    		sort(v.begin(),v.end(),greater<int>());int id=0;
    		for(;id<(int)v.size()&&v[id]>=id+1;id++);
    		dp[x][k]=id;
    	}
    }
    inline void dfs1(int x,int fa)
    {
    	for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) dfs1(e[i].to,x);
    	for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
    		for(int k=1;k<20;k++) dp[x][k]=max(dp[x][k],dp[e[i].to][k]);
    }
    int main()
    {
    	read(n);for(int i=1,x,y;i<n;i++) read(x),read(y),adde(x,y),adde(y,x);
    	dfs0(1,0),dfs1(1,0);for(int i=1;i<=n;i++) for(int j=1;j<20;j++) rs+=max(dp[i][j]-1,0);
    	return printf("%lld\n",rs),0;
    }
    
  • 相关阅读:
    sql server 数据定义常用sql
    历史数据处理
    Border不响应MouseDown等点击事件
    wpf 打印A3 & A4 纸张大小的图片
    xml文件读写
    请使用 MakeValid 将该实例转换为有效实例
    接口和抽象类
    索引器&集合
    C#中重写、重载和隐藏的概念
    超图SuperMap iObjects .NET Geometry转geoJSON&WKT&WKB
  • 原文地址:https://www.cnblogs.com/pealfrog/p/15110050.html
Copyright © 2020-2023  润新知