博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LOJ 2546 「JSOI2018」潜入行动——树形DP
阅读量:4974 次
发布时间:2019-06-12

本文共 2497 字,大约阅读时间需要 8 分钟。

题目:

dp[ i ][ j ][ 0/1 ][ 0/1 ] 表示 i 子树,用 j 个点,是否用 i , i 是否被覆盖。

注意 s1<=s0 ,别弄出负角标。

用 if 判断一下,如果有值再转移,会快非常多。

复杂度是 O(n*k) 的。证明:

先约定如果一个小于 k 的子树和一个大于 k 的子树合并,在小于 k 的子树那里看复杂度。

1.两个小于 k 的子树 cr 和 v 合并,且合并完之后还是小于 k 的;

  对于 cr 里的每个点,要和 v 的每个点产生贡献。虽然和很多 v 都这样做了,但这些 v 的大小加起来小于 k (因为规定合并完还是小于 k ),所以一个点贡献 O(k) 次。

  如果合并完大于 k ,就在 “一个小于 k 的子树和一个大于 k 的子树合并” 的部分考虑复杂度了。

2.一个小于 k 的子树 cr 和一个大于 k 的子树 v 合并。

  对于 cr 里的每个点,此时都要进行 O(k) 次贡献。合并完之后 cr 的大小变成大于 k ,所以这种贡献,每个点只会经历一次。

3.一个大于 k 的子树 cr 和一个大于 k 的子树 v 合并。

  产生 k2 的贡献。如果是两个大小为 k 的子树,合并之后大小变成 2*k ;再合并进来一个大小为 k 的,大小就变成 3*k ;即这种合并最多 \( \frac{n}{k} \) 次。

综上,复杂度是 O(n*k) 的。

#include
#include
#include
#define ll long longusing namespace std;int rdn(){ int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){
if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret;}int Mx(int a,int b){
return a>b?a:b;}int Mn(int a,int b){
return a
=mod)x-=mod;while(x<0)x+=mod;return x;}int n,k,hd[N],xnt,to[N<<1],nxt[N<<1];int siz[N],dp[N][M][2][2],tp[2][2];void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}void cz(int &x,int y){x=upt(x+y);}void dfs(int cr,int fa){ dp[cr][0][0][0]=dp[cr][1][1][0]=1; siz[cr]=1; for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa) { dfs(v,cr); for(int s0=Mn(k,siz[cr]+siz[v]);s0>=0;s0--) { tp[0][0]=tp[0][1]=tp[1][0]=tp[1][1]=0; for(int s1=Mx(0,s0-siz[cr]),lm=Mn(s0,Mn(siz[v],k));s1<=lm;s1++) { int d=s0-s1; if(dp[cr][d][0][0]) { cz(tp[0][0],(ll)dp[cr][d][0][0]*dp[v][s1][0][1]%mod); cz(tp[0][1],(ll)dp[cr][d][0][0]*dp[v][s1][1][1]%mod); } if(dp[cr][d][0][1]) cz(tp[0][1],(ll)dp[cr][d][0][1]*(dp[v][s1][0][1]+dp[v][s1][1][1])%mod); if(dp[cr][d][1][0]) { cz(tp[1][0],(ll)dp[cr][d][1][0]*(dp[v][s1][0][0]+dp[v][s1][0][1])%mod); cz(tp[1][1],(ll)dp[cr][d][1][0]*(dp[v][s1][1][0]+dp[v][s1][1][1])%mod); } if(dp[cr][d][1][1]) { cz(tp[1][1],(ll)dp[cr][d][1][1] *((ll)dp[v][s1][0][0]+dp[v][s1][0][1]+dp[v][s1][1][0]+dp[v][s1][1][1])%mod); } } for(int f0=0;f0<=1;f0++) for(int f1=0;f1<=1;f1++) dp[cr][s0][f0][f1]=tp[f0][f1]; } siz[cr]+=siz[v]; }}int main(){ n=rdn();k=rdn(); for(int i=1,u,v;i

 

转载于:https://www.cnblogs.com/Narh/p/10749068.html

你可能感兴趣的文章
LeetCode 题解之Add Digits
查看>>
hdu1502 , Regular Words, dp,高精度加法
查看>>
SpringBoot在idea中的热部署配置
查看>>
MyEclipse连接SQL Server 2008数据库的操作方法
查看>>
JS验证图片格式和大小并预览
查看>>
laravel5.2 移植到新服务器上除了“/”路由 ,其它路由对应的页面显示报404错误(Object not found!)———新装的LAMP没有加载Rewrite模块...
查看>>
编写高质量代码--改善python程序的建议(六)
查看>>
windows xp 中的administrator帐户不在用户登录内怎么解决?
查看>>
接口和抽象类有什么区别
查看>>
Codeforces Round #206 (Div. 2)
查看>>
**p
查看>>
优先队列详解
查看>>
VS2012 创建项目失败,,提示为找到约束。。。。
查看>>
设计类图
查看>>
类对象
查看>>
[Voice communications] 声音的滤波
查看>>
SQL语言之概述(一)
查看>>
软件建模——第9章 毕业论文管理系统—面向对象方法
查看>>
[SDOI2008]洞穴勘测
查看>>
Difference between Linearizability and Serializability
查看>>