• 【JSOI2018】潜入行动


    【JSOI2018】潜入行动

    img
    img
    img
    img

    树形(DP)。设(f_{i,j,0/1,0/1})表示以(i)为根的子树中,用了(j)个监听器,是否放置了监听器,是否被监听的方案数。转移就多讨论几种情况就好了。

    关键问题是直接这么做是(O(NK^2))的。

    解决方案就是,我们别(DP)边维护子树的大小(size),每次枚举子树的监听器的时候不能超过(min{size,k})。这看似只是一个常数优化,但其实可以吧复杂度降到(O(NK))

    可以看这位dalao的证明

    (DP)的时候按合并的两个子树大小分为三种情况:

    1. 两个子树(size)均大于(k)
    2. 其中一个大于(k),另一个小于等于(k)
    3. 两个子树大小均小于等于(k)

    然后对于复杂度的分析:

    1. 合并一次(O(k^2)),但是最多这样合并(frac{n}{k})次。
    2. 考虑小的那颗子树每个点贡献一次复杂度。并且以后不会再贡献,因为合并后子树大小大于(k)了。
    3. 考虑每个点贡献的次数就是每个合并时另一颗子树的大小。所以每个点最多贡献(K)次。

    所以复杂度(O(NK))

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=1e9+7;
    int n,k;
    struct road {int to,next;}s[N<<1];
    int h[N],cnt;
    void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}
    ll f[N][105][2][2];
    ll g[105][2][2];
    int size[N];
    
    void dfs(int v,int fr) {
    	size[v]=1;
    	f[v][0][0][0]=1;
    	f[v][1][1][0]=1;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==fr) continue ;
    		dfs(to,v);
    		int lim=min(k,size[v]);
    		memset(g,0,sizeof(g));
    		for(int j=0;j<=lim;j++) {
    			int lim2=min(size[to],k-j);
    			for(int q=0;q<=lim2;q++) {
    				(g[j+q][0][0]+=f[v][j][0][0]*f[to][q][0][1])%=mod;
    				(g[j+q][0][1]+=f[v][j][0][1]*(f[to][q][0][1]+f[to][q][1][1])+f[v][j][0][0]*f[to][q][1][1])%=mod;
    				(g[j+q][1][0]+=f[v][j][1][0]*(f[to][q][0][1]+f[to][q][0][0]))%=mod;
    				(g[j+q][1][1]+=f[v][j][1][1]*(f[to][q][0][0]+f[to][q][0][1]+f[to][q][1][0]+f[to][q][1][1]))%=mod;
    				(g[j+q][1][1]+=f[v][j][1][0]*(f[to][q][1][0]+f[to][q][1][1]))%=mod;
    			}
    		}
    		memcpy(f[v],g,sizeof(g));
    		size[v]+=size[to];
    	}
    }
    
    int main() {
    	n=Get(),k=Get();
    	int a,b;
    	for(int i=1;i<n;i++) {
    		a=Get(),b=Get();
    		add(a,b),add(b,a);
    	}
    	dfs(1,0);
    	int ans=0;
    	ans=(ans+f[1][k][0][1]+f[1][k][1][1])%mod;
    	cout<<ans;
    	return 0;
    }
    
    
  • 相关阅读:
    You Will Be Memorizing Things
    PowerShell与cmd
    select的一些问题。
    深刻理解数据库外键含义
    html居中问题
    jsp中嵌入的html
    jdbc连接mysql报错:com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '金厉旭' in 'field list'
    [kuangbin带你飞]专题一 简单搜索
    算法竞赛训练指南11.2 最小生成树
    [kuangbin带你飞]专题六 最小生成树
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10512855.html
Copyright © 2020-2023  润新知