首先先看今年JSOI 2018 的DAY1T1 潜入行动,由于n*k^2的算法能够水过,所以这个DP不是非常难想,先上题面。
https://loj.ac/problem/2546 LOJ题目链接
我们可以想到的DP状态是DP[i][j][0/1][0/1]表示以i为根的子树内,我们放了j个监听器,i点是否放置监听器,i点是否能被监听到的方案数,那显然的是可以使用乘法原理类似树形背包转移,复杂度O(n*k^2),但是可以优化,我们可以使用DFS序DP 复杂度O(n*k),但是本人太蒻啦,不是很会,不多介绍了。
POI 2014 HOT-HOTELS
题意:
有一个树形结构,每条边的长度相同,任意两个节点可以相互到达。选3个点。两两距离相等。有多少种方案?
这道题和以往见到的大多数树形DP不是很一样,通常树形DP表示的是以每一个点为根的子树内的方案,但是这道题我们通过枚举中间点来进行方案计数,我们先枚举一个点做根,然后统计所有到达这个根距离相同的三点的个数。复杂度O(n^2)
BZOJ上有加强版,做法不是很一样,需使用重链剖分。
IOI 2005 RIV 河流
题意 : 洛谷链接: https://www.luogu.org/problemnew/show/P3354
题目描述:
几乎整个Byteland王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫Bytetown。
在Byteland国,有n个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown的伐木场。Byteland的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场。这k个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。
注:所有的河流都不会分叉,形成一棵树,根结点是Bytetown。
国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米1分钱。
题解:我们看到这道题首先考虑设出DP状态为,DP[i][j]来表示i这棵子树里新建了j个伐木场所需要的最小运费,但是这样是错的,为什么呢?如果我们在i的不同祖先节点设置了伐木场那么i子树对答案的贡献就会发生变化,因为距离改变了,那我们考虑如何设状态呢? 我们设DP[i][j][k][0/1] 来表示i这个子树,离其最近的建了伐木场的祖先为j,子树内修建了k个伐木场,i这个节点是否建立了伐木场,最小花费是多少。然后我们就可以转移了嘻嘻嘻。方程如下,略有复杂。
for(register int j=1;j<=top;++j)
{
//第一维 now当前节点,第二维第一个建立伐木场的祖先,第三维子树里建立了几个伐木场,第四维now节点是否建立伐木场
for(register int p=k;p>=0;--p)
{
f[now][sta[j]][p][0]+=f[to][sta[j]][0][0];
f[now][sta[j]][p][1]+=f[to][now][0][0];
for(register int t=0;t<=k;++t)
{
if(t>p) continue;
f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0],f[now][sta[j]][p-t][0]+f[to][sta[j]][t][0]);
f[now][sta[j]][p][1]=min(f[now][sta[j]][p][1],f[now][sta[j]][p-t][1]+f[to][now][t][0]);
}
}
}
for(register int j=1;j<=top;++j)
{
for(register int p=0;p<=k;++p)
{
if(p>=1)
f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0]+(dep[now]-dep[sta[j]])*w[now],f[now][sta[j]][p-1][1]);
else
{
f[now][sta[j]][p][0]+=(dep[now]-dep[sta[j]])*w[now];
}
}
}
top--;
JSOI 2018 潜入行动 代码:
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
const int maxn = 2e5;
const int mod = 1e9+7;
struct node{
int to,next;
}e[1000010];
inline int read(){int w=1,s=0;char ch=getchar();while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}return w*s;}
int h[maxn],n,k,tot,size[maxn];
unsigned dp[maxn][110][2][2],tmp[10010][2][2];
inline void add(int from,int to){e[++tot].next=h[from],e[tot].to=to,h[from]=tot;}
void dfs(int now,int fa)
{
size[now]=1;
dp[now][1][1][0]=1;dp[now][0][0][0]=1;
for(register int i=h[now];i;i=e[i].next)
{
int v=e[i].to;if(v==fa) continue;
dfs(v,now);
int minn=min(size[now],k);
for(register int i=0;i<=minn;++i)
{
tmp[i][0][0]=dp[now][i][0][0];tmp[i][1][0]=dp[now][i][1][0];tmp[i][0][1]=dp[now][i][0][1];tmp[i][1][1]=dp[now][i][1][1];
dp[now][i][0][1]=0;dp[now][i][1][0]=0;dp[now][i][1][1]=0;dp[now][i][0][0]=0;
}
for(register int i=0;i<=minn;++i)
{
for(register int j=0,maxx=min(size[v],k-i);j<=maxx;++j)
{
dp[now][i+j][0][1]+=(long long)(1ll*(dp[v][j][0][1]+dp[v][j][1][1])%mod*tmp[i][0][1]%mod+1ll*tmp[i][0][0]*(dp[v][j][1][1])%mod)%mod;
dp[now][i+j][0][1]%=mod;
dp[now][i+j][1][1]+=(long long )(1ll*tmp[i][1][1]*(dp[v][j][0][1]+dp[v][j][1][1]+dp[v][j][0][0]+dp[v][j][1][0])%mod+1ll*tmp[i][1][0]*(dp[v][j][1][1]+dp[v][j][1][0])%mod)%mod;
dp[now][i+j][1][1]%=mod;
dp[now][i+j][0][0]+=(long long)(1ll*tmp[i][0][0]*dp[v][j][0][1]%mod);
dp[now][i+j][0][0]%=mod;
dp[now][i+j][1][0]+=(long long )(1ll*tmp[i][1][0]*(dp[v][j][0][1]+dp[v][j][0][0])%mod)%mod;
dp[now][i+j][1][0]%=mod;
}
}
size[now]+=size[v];
}
}
int main(){
n=read();k=read();
for(register int i=1;i<n;++i)
{
int x,y;x=read(),y=read();add(x,y);add(y,x);
}
dfs(1,0);
cout<<(dp[1][k][1][1]+dp[1][k][0][1])%mod;
return 0;
}
IOI 2005 河流 代码:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e2;
long long n,w[maxn],h[maxn],k,tot,f[maxn][maxn][maxn][2],fa[maxn],dep[maxn],sta[maxn],top;
struct node{
long long to,next,w;
}e[maxn<<2];
inline void add(int from,int to,int w){e[++tot].next=h[from],e[tot].to=to,h[from]=tot,e[tot].w=w;}
void dfs(int now)
{
sta[++top]=now;
for(register int i=h[now];i;i=e[i].next)
{
int to=e[i].to;dep[to]=dep[now]+e[i].w;dfs(to);
for(register int j=1;j<=top;++j)
{
//第一维 now当前节点,第二维第一个建立伐木场的祖先,第三维子树里建立了几个伐木场,第四维now节点是否建立伐木场
for(register int p=k;p>=0;--p)
{
f[now][sta[j]][p][0]+=f[to][sta[j]][0][0];
f[now][sta[j]][p][1]+=f[to][now][0][0];
for(register int t=0;t<=k;++t)
{
if(t>p) continue;
f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0],f[now][sta[j]][p-t][0]+f[to][sta[j]][t][0]);
f[now][sta[j]][p][1]=min(f[now][sta[j]][p][1],f[now][sta[j]][p-t][1]+f[to][now][t][0]);
}
}
}
}
for(register int j=1;j<=top;++j)
{
for(register int p=0;p<=k;++p)
{
if(p>=1)
f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0]+(dep[now]-dep[sta[j]])*w[now],f[now][sta[j]][p-1][1]);
else
{
f[now][sta[j]][p][0]+=(dep[now]-dep[sta[j]])*w[now];
}
}
}
top--;
}
int main()
{
cin>>n>>k;
for(register int i=1,u;i<=n;++i)
{
int v;
cin>>w[i]>>fa[i]>>v; add(fa[i],i,v);
}
dfs(0);
cout<<f[0][0][k][0];
return 0;
}
POI 2014 HOT-hotels
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
struct node{
int to,next;
}e[1000010];
inline int read(){int w=1,s=0;char ch=getchar();while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}return w*s;}
ll f[5050],tot,h[1000010],n,m,g[5050],ans,dep[5001],tmp[5010],maxdep;
inline void add(int from,int to){e[++tot].next=h[from];h[from]=tot;e[tot].to=to;}
void dfs(int now,int fa)
{
tmp[dep[now]]++;
maxdep=max(maxdep,dep[now]);
for(register int i=h[now];i;i=e[i].next)
{
int to=e[i].to; if(to==fa) continue; dep[to]=dep[now]+1;
dfs(to,now);
}
}
inline void calc()
{
for(register int i=1;i<=maxdep;++i)
{
ans+=g[i]*tmp[i];
g[i]+=tmp[i]*f[i];
f[i]+=tmp[i];
}
for(register int i=1;i<=maxdep;++i) tmp[i]=0;
}
int main()
{
n=read();
for(register int u,v,i=1;i<n;++i) u=read(),v=read(),add(u,v),add(v,u);
for(register int now=1;now<=n;++now){memset(f,0,sizeof(f));memset(g,0,sizeof(g));for(register int i=h[now];i;i=e[i].next){int to=e[i].to;dep[to]=1;dfs(to,now);calc();}}
cout<<ans;
return 0;
}