习题:乃爱与城市拥挤程度(树形DP)
题目
思路
暴力做法大家都会,而且分还不低
考试的时候80分,以为卡一下常就过了
结果发现是数据出锅了,本来设计的暴力分为30
但是暴力其实与这道题的正解关系很大
当你打暴力的时候,你会发现很多问题被重复求解
DP自然而然的出现在代码之中
- 子问题1
怎么快速统计个数
设\(dp[i][j]\)为以i号节点的子树且距离小于k的城市数总和
dp的初始值为1,因为不管k为多少,都至少有它本身一个
\(dp[u][k]=1+\sum_{i}^{i\in son} dp[v][k-1]\)
注意到DP的定义,我们只会统计到子树,
我们并没有考虑到u号节点的父节点和祖先节点
但是通过u号节点的祖先节点可以间接的求出答案
每一个的贡献即\(dp[fa][z]-dp[son][z-1]\)
fa为son的唯一一个父亲,son的初值为u
综上,第一个子问题就解决了
- 子问题2
怎么快速统计拥挤度
这个时候,我们发现更新u号节点的时候,需要用到距离u号节点为j的总数
也就是说,我们对DP的定义的就需要发生改变
\(dp[i][j]\)为i号节点的距离为k的值
\(dp[u][j]=\sum_{i}^{i \in son}dp[i][j-1]\)
但是此刻选出的值不是真正的只考虑子树的值
同子问题1,可以通过父亲节点和祖先节点间接地求出
每一个的贡献\(dp[fa][z]\over dp[son][z-1]\)
son和fa的定义同子问题1
代码
#include<iostream>
#include<vector>
using namespace std;
const int mod=1e9+7;
int n,k;
int fat[100005];
int dp1[100005][15];
int dp2[100005][15];
int f1[100005];
int f2[100005];
vector<int> g[100005];
int qkpow(int a,int b)
{
if(b==0)
return 1;
if(b==1)
return a;
int t=qkpow(a,b/2);
t=(1ll*t*t)%mod;
if(b%2==1)
t=(1ll*a*t)%mod;
return t;
}
void solve1(int u,int fa)
{
fat[u]=fa;
for(int i=0;i<=k;i++)
{
dp1[u][i]=dp2[u][i]=1;
}
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v!=fa)
{
solve1(v,u);
for(int j=1;j<=k;j++)
{
dp1[u][j]+=dp1[v][j-1];
dp2[u][j]=((1ll*dp2[u][j]*dp2[v][j-1]%mod)*dp1[v][j-1])%mod;
}
}
}
}
void solve2(int u,int fa)
{
int now=u;
int fa_num[15]={};
f1[u]=fa_num[k]=dp1[u][k];
int z=k;
while(--z&&fat[now])
{
f1[u]=(f1[u]+dp1[fat[now]][z]-dp1[now][z-1]);
fa_num[z]=f1[u];
now=fat[now];
}
if(fat[now])
f1[u]++;
z=k;
now=u;
f2[u]=(1ll*dp2[u][k]*f1[u])%mod;
while(--z&&fat[now])
{
f2[u]=((1ll*f2[u]*dp2[fat[now]][z]%mod)*qkpow(1ll*dp2[now][z-1]*dp1[now][z-1]%mod,mod-2)%mod)%mod;
f2[u]=(1ll*f2[u]*(f1[u]-fa_num[z+1]))%mod;
now=fat[now];
}
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v!=fa)
solve2(v,u);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<n;i++)
{
int s,e;
cin>>s>>e;
g[s].push_back(e);
g[e].push_back(s);
}
solve1(1,0);
solve2(1,0);
for(int i=1;i<=n;i++)
cout<<f1[i]<<' ';
cout<<'\n';
for(int i=1;i<=n;i++)
cout<<f2[i]<<' ';
return 0;
}
题外话
作为一个舰长,
我必须站出来,并大声喊出自己的理想
德丽莎世界第一可爱!!!

浙公网安备 33010602011771号