首页 > 其他分享 >Codeforces 1097 G Vladislav and a Great Legend 题解 (DP)

Codeforces 1097 G Vladislav and a Great Legend 题解 (DP)

时间:2022-12-24 12:03:35浏览次数:52  
标签:sz Great 1097 题解 pos size 节点 dp MOD

题目链接

思路

首先看到这种求\(x^k\)形式的,直接无脑转下降幂:\(x^k=\sum_{i=1}^k S2(k,i)\cdot x^{\underline{i}}\),其中\(S2\)表示第二类斯特林数,\(x^{\underline i}\)表示\(x\cdot(x-1)\cdots(x-i+1)\)。这是一个恒等式。

这题由于\(k\le 200\),所以可以直接\(O(k^2)\)暴力求出斯特林数。假设现在已经确定了点集\(s\),要求\(f_s\)。由于\(x^{\underline i}=\binom xi \cdot i!\),所以只要对所有\(1\le i\le k\),求出在点集\(s\)构成的虚树中选\(i\)条边的方案数就可以了。考虑对所有的点集\(s\)一起计算答案,我们希望能求出一个数组\(g_i\)表示在树上选择一个大小为\(i\)的边集,并再选一个点集使得这个点集构成的虚树包含边集中的所有边的方案数。算出\(g\)之后就能直接用斯特林数求答案了。

假设现在已经选好了边集,什么样的点集构成的虚树才能包含里面所有的边?每条边都是有两个端点的,一个深度大一个深度小,把深度小的称为上节点,另一个称为下节点。首先找出边集内所有满足"下节点的子树内没有任何被选择的边"的这些边,它们的下节点的子树内(包括下节点本身)必须有至少一个点被选(条件1)。然后再看存不存在一条被选的边满足所有其他被选边都在它的下节点的子树内,如果存在,它的下节点子树外必须有至少一个点被选(条件2)。发现这两个条件合起来就是充要的。到这里应该都不难想。

然后就可以\(dp\)了,\(dp_{i,j}\)表示在点\(i\)的子树中,所有点的父边(包括i)一共有\(j\)条边被选,且选的点集满足条件1的方案数。转移的时候是对所有的儿子做一个背包。\(dp\)的总复杂度看似是\(O(nk^2)\)的,其实是\(O(nk)\)的,这题的重点就是知道这个方法复杂度是对的并且敢写。复杂度证明后面会说。统计答案的话,按照条件2中的那种边存不存在分两种情况讨论,dp的时候对每个节点顺便统计一下即可。

重点:复杂度证明

官方题解写得很不负责,直接就说跟那种两个节点只在LCA处贡献复杂度的证明方式类似,其实根本不一样。

我们要证明的就是总计算量是\(O(nk)\)级别的。首先把树的形态改造一下,容易发现改造之后每个点都只有最多两个儿子,且树中最多有\(2n\)个节点,且对这个树dp的计算量不比之前少,所以对改造之后的树证明复杂度即可。

首先对于任意一个节点做背包的计算量是\(min(k,size(ls))\cdot min(k,size(rs))\)。把节点分成两类,证明这两类节点的总计算量都是\(O(nk)\):

  • 有任意一个儿子的子树大小达到k的。令\(p=min(k,size(ls),size(rs))\),则这个点的计算量为\(kp\)。我们做dp是在树中从下往上计算的,处理完这个节点后,上面再用到这个节点的子树大小贡献复杂度时,也只会贡献\(min(k,k+p)=k\),相当于有\(p\)的大小"消失"了,被消耗掉了。并且发现每一个点贡献的大小只能被消耗一次。所以这部分的总计算量是\(O(nk)\)。
  • 其余节点。发现有任意一个儿子的子树大小达到k的节点其实构成了树上包含根的一个连通块,而其余节点是挂在连通块下面的一些子树。把每个这种子树分别拿出来计算复杂度,对于任意一个子树,其中的两个点只会在LCA处贡献复杂度,所以子树内部计算量最多\(size^2\)。而所有子树的\((\sum size)\le n\),且所有这种子树的\(size\)都是\(O(k)\)级别的(因为子树根的两个儿子大小都达不到k)。所以总计算量仍然是\(O(nk)\)。证毕。

这题的总复杂度也就是\(O(nk)\)。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

const LL MOD=1e9+7;

LL qpow(LL x,LL a)
{
	LL res=x,ret=1;
	while(a>0)
	{
		if(a&1) (ret*=res)%=MOD;
		a>>=1;
		(res*=res)%=MOD;
	}
	return ret;
}

int dp[100010][210],dp2[100010][210][3];
LL n,t,sz[100010],pw2[100010],f[100010],s2[210][210];
vector <LL> g[100010];

void dfs(LL pos,LL par)
{
  sz[pos]=1;
  vector <LL> son;
  rep(i,g[pos].size()) if(g[pos][i]!=par)
  {
    dfs(g[pos][i],pos);
    sz[pos]+=sz[g[pos][i]];
    son.pb(g[pos][i]);
  }
  rep(i,son.size()+3) rep(j,t+3) rep(k,3) dp2[i][j][k]=0;
  dp2[0][0][0]=1;
  LL cursum=0;
  rep(i,son.size())
  {
    rep(j,min(cursum+1,t+1)) rep(k,3) if(dp2[i][j][k])
    {
      (dp2[i+1][j][k]+=(LL)dp2[i][j][k]*pw2[sz[son[i]]]%MOD)%=MOD;
      repn(p,min(sz[son[i]],t))
      {
        if(j+p>t) break;
        (dp2[i+1][j+p][min(2,k+1)]+=(LL)dp2[i][j][k]*dp[son[i]][p]%MOD)%=MOD;
      }
    }
    cursum+=sz[son[i]];
  }
  repn(i,t) dp[pos][i]=(dp2[son.size()][i][1]+dp2[son.size()][i][2])%MOD;
  repn(i,t) (dp[pos][i]*=2LL)%=MOD;
  repn(i,t-1)
  {
    LL val=(LL)dp[pos][i]*(pw2[n-sz[pos]]-1+MOD);
    (f[i+1]+=val)%=MOD;
  }
  for(int i=2;i<=t;++i) (f[i]+=(LL)dp2[son.size()][i][2]*pw2[n-sz[pos]]%MOD*2)%=MOD;
  for(int i=t-1;i>0;--i) (dp[pos][i+1]+=dp[pos][i])%=MOD;
  (dp[pos][1]+=(pw2[sz[pos]]-1+MOD)%MOD)%=MOD;
  (f[1]+=(pw2[sz[pos]]-1+MOD)*(pw2[n-sz[pos]]-1+MOD))%=MOD;
}

int main()
{
  fileio();

  pw2[0]=1;repn(i,100005) pw2[i]=pw2[i-1]*2%MOD;
  cin>>n>>t;
  LL x,y;
  rep(i,n-1)
  {
    scanf("%lld%lld",&x,&y);
    g[x].pb(y);g[y].pb(x);
  }
  dfs(1,0);
  s2[1][1]=1;
  for(int i=2;i<=t;++i)
  {
    s2[i][1]=s2[i][i]=1;
    for(int j=2;j<i;++j) s2[i][j]=(s2[i-1][j-1]+s2[i-1][j]*j)%MOD;
  }
  LL ans=0,fac=1;
  repn(i,t)
  {
    (fac*=i)%=MOD;
    (ans+=f[i]*s2[t][i]%MOD*fac)%=MOD;
  }
  cout<<ans<<endl;

  termin();
}

114行的臭代码(恼

标签:sz,Great,1097,题解,pos,size,节点,dp,MOD
From: https://www.cnblogs.com/legendstane/p/codeforces-1097-g-Vladislav-and-a-Great-Legend-solut

相关文章

  • Codeforces 1097 G Vladislav and a Great Legend 题解 (DP)
    题目链接思路首先看到这种求\(x^k\)形式的,直接无脑转下降幂:\(x^k=\sum_{i=1}^kS2(k,i)\cdotx^{\underline{i}}\),其中\(S2\)表示第二类斯特林数,\(x^{\underlinei}\)表......
  • Codeforces 1097 G Vladislav and a Great Legend 题解 (DP)
    题目链接思路首先看到这种求\(x^k\)形式的,直接无脑转下降幂:\(x^k=\sum_{i=1}^kS2(k,i)\cdotx^{\underline{i}}\),其中\(S2\)表示第二类斯特林数,\(x^{\underlinei}\)表......
  • Codeforces 1097 G Vladislav and a Great Legend 题解 (DP)
    题目链接思路首先看到这种求\(x^k\)形式的,直接无脑转下降幂:\(x^k=\sum_{i=1}^kS2(k,i)\cdotx^{\underline{i}}\),其中\(S2\)表示第二类斯特林数,\(x^{\underlinei}\)表......
  • luoguP5383 普通多项式转下降幂多项式 题解
    学习了bztMinamoto大佬的做法,希望这篇题解可以使得那个方法更加易于理解。既然下降幂多项式转普通多项式可以采取分治\(\operatorname{NTT}\),那么可以猜测逆过来也可以......
  • CF1765J Hero to Zero 题解
    题意给出长为\(n\)数组\(a,b\),令\(c_{i,j}=|a_i-b_j|\)。每次可以花\(1\)的代价让矩阵\(c\)的一行或一列或一个格子减\(1\),也可以花\(-1\)的代价让一行或一列......
  • # Win10为知笔记Docker镜像部署 -v /wiz/storage问题解决
    Win10为知笔记Docker镜像部署-v/wiz/storage问题解决用了很长一段时间的为知笔记,客户端体验还行,服务端笔记同步体验不佳。准备用Docker自己搭一个服务端。环境:操作......
  • 前端竞态问题解决方法
    主要是通过 AbortController来终止前一个请求。例如:useEffect(()=>{//创建controllerconstcontroller=newAbortController();//将controller作......
  • uniapp配合xcode打包遇到videoPlayer module is not added的问题解决
     这个情况是因为没有配置相关插件,虽然在uniapp中提示添加但是这对于我们自己xcode打包毫无意义,这儿配置的很多东西都是给uniapp云端打包提示添加对应功能的xcode本地打......
  • CF1537D 题解
    前言题目传送门!更好的阅读体验?遇到这种博弈论的题目,当然是要打表找规律了!思路首先,很容易想到暴力递推。代码在上方的链接里。然后看几眼就能发现,在大部分情况下,如果......
  • Codeforces 1630 E Expected Components 题解 (组合数学)
    题目链接首先明确概念:排列。指的就是一个把数组a重排得到的序列,两个排列相等当且仅当它们对应位全都相等环形排列。指的是把数组a重排得到的序列首尾相接得到的环形数......