• 【洛谷4516】[JSOI2018] 潜入行动(树上背包)


    点此看题面

    大致题意: 给定一棵树,让你选择恰好(k)个点,使得与每个点相邻的节点(不包括该节点自身)中都有至少一个被选中的点,求方案数。

    树上背包

    感觉就是一道裸题啊。。。

    树上背包的题目嘛,看着像是(O(nk^2)),实际上就是(O(nk))。(这个复杂度我至今不会证

    直接设(f_{i,j,0/1,0/1})表示在(i)的子树中选了(j)个点、是否选择(i)(i)是否已满足条件时,(i)子树中所有点(不包括(i)自身)满足条件的方案数。

    转移的式子又臭又长,放上来不太美观,不过个人感觉还是很容易推的。

    因此直接上代码了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define K 100
    #define X 1000000007
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    using namespace std;
    int n,m,ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    class TreeDP
    {
    	private:
    		int f[N+5][K+5][2][2],g[N+5],t[K+5][2][2];
    		I void DP(CI x,CI lst=0)//树上背包
    		{
    			f[x][0][0][0]=f[x][1][1][0]=g[x]=1;//初始化
    			for(RI i=lnk[x],y,j,k;i;i=e[i].nxt) if((y=e[i].to)^lst)//枚举子节点
    			{
    				for(DP(y,x),j=0;j<=min(g[x],m);++j) t[j][0][0]=f[x][j][0][0],
    					t[j][0][1]=f[x][j][0][1],t[j][1][0]=f[x][j][1][0],t[j][1][1]=f[x][j][1][1],//复制一份
    					f[x][j][0][0]=f[x][j][0][1]=f[x][j][1][0]=f[x][j][1][1]=0;//清空
    				for(j=0;j<=min(g[x],m);++j) for(k=0;k<=min(g[y],m-j);++k)//枚举,注意上界
    					Inc(f[x][j+k][0][0],1LL*t[j][0][0]*f[y][k][0][1]%X),//0 0
    					Inc(f[x][j+k][0][1],(1LL*t[j][0][0]*f[y][k][1][1]+
    						1LL*t[j][0][1]*(f[y][k][0][1]+f[y][k][1][1]))%X),//0 1
    					Inc(f[x][j+k][1][0],1LL*t[j][1][0]*(f[y][k][0][0]+f[y][k][0][1])%X),//1 0
    					Inc(f[x][j+k][1][1],(1LL*t[j][1][0]*(f[y][k][1][0]+f[y][k][1][1])+
    						1LL*t[j][1][1]*(0LL+f[y][k][0][0]+f[y][k][0][1]+f[y][k][1][0]+f[y][k][1][1]))%X);//1 1
    				g[x]+=g[e[i].to];//更新子树大小
    			}
    		}
    	public:
    		I void Solve() {DP(1),printf("%d
    ",(f[1][m][0][1]+f[1][m][1][1])%X);}//注意最终答案
    }T;
    int main()
    {
    	RI i,x,y;for(scanf("%d%d",&n,&m),i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);//读边建树
    	return T.Solve(),0;
    }
    
  • 相关阅读:
    zend guard 4/5 破解版和免过期办法,已补授权Key一枚,成功注册。
    一身冷汗,PHP应该禁用的函数
    CentOS 5.5 安装和卸载桌面
    php加速模块 eAccelerator open_basedir错误解决办法
    配置电信网通双线双IP的解决办法
    php安装igbinary模块
    ubuntu系统VNC服务器安装配置
    python3 之 闭包实例解析 Be
    python3 之 内置函数enumerate Be
    python3 之 匿名函数 Be
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4516.html
Copyright © 2020-2023  润新知