[JSOI2018]潜入行动 解题报告

Posted BruceW

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JSOI2018]潜入行动 解题报告相关的知识,希望对你有一定的参考价值。

[JSOI2018]潜入行动 解题报告

链接

题面

题目大意

一棵节点为 (n) 的树, 有 (k) 个装置. 当点 (u) 上安装了装置后, 对于所有 ((u,v)), 点 (v) 都会被覆盖.

要求每个点上最多只能安装一个装置, (k) 个装置必须被用完.

求树上所有节点都被覆盖的方案数.

数据范围

(1le n le 10^5,1 le k le min(n,100)).


思路

容易想到一个 (DP), 设 (f[u][j][0/1/2/3]) 分别表示点 (u) 处于 (0/1/2/3) 状态时, 子树中安装了 (j) 个装置的方案数. (0/1/2/3) 分别的意义如下.

  • (0) : 点 (u) 上没有安装装置, 也没有被覆盖.
  • (1) : 点 (u) 上没有安装装置, 但被覆盖了.
  • (2) : 点 (u) 上安装了装置, 但没有被覆盖.
  • (3) : 点 (u) 上安装了装置, 并且被覆盖了.

转移的时候要保证 (u) 的子树中除了 (u) 以外的点都被覆盖了.

并且转移的时候先用一个 (tmp) 数组来记录新的 (f) 值, 避免转移顺序发生混乱. (树形背包的常用做法)

这个 (DP) 看上去是 (O(nk^2)) 的, 实际上可以证明是 (O(nk)) 的.

假设我们现在需要合并的两个背包为 (f[u])(f[v]), 我们分三种情况讨论.

  1. $sz[u] ge k m and $$sz[v] ge k$ : 这时合并的复杂度是 (O(k^2)), 但这种情况只会出现 (Oleft(frac{n}{k} ight)) 次, 所以总复杂度还是 (O(nk)).
  2. $sz[u] ge k m and $$sz[v] < k$ : 这时点 (v) 的子树中的每个点都会贡献 (O(k)) 的复杂度, 而每个点最多都只会在它的祖先节点上经历这样一次合并, 所以总贡献的时间复杂度也是 (O(nk)).
  3. $sz[u]<k m ans $$sz[v]<k$ : 这时, 对于点 (u) 子树中的每个点, 每次合并的贡献是 (sz[v]), 而 (sum sz[v] le k) (因为若大于 (k) 的话就不满足 (sz[u]<k) 了), 所以这种情况下每个点的总贡献为 (O(k)), 所有点的总贡献为 (O(nk)).

在上述三种情况下, 时间复杂度都是 (O(nk)), 所以总时间复杂度也是 (O(nk)).

但由于常数比较大 (转移方程比较复杂), 并且空间给的比较小 ((250)MB, 不能开 (long long)), 所以需要卡卡常.


代码

#include<bits/stdc++.h>

using namespace  std;

#define pb push_back
#define sz(x) (int)(x).size()
typedef long long ll;

const int _=1e2+7;
const int __=1e5+7;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;

bool be;
int n,K;
int f[__][_][4],tmp[_][4];
vector<int> to[__];
bool en;

void Init(){
    cin>>n>>K;
    int x,y;
    for(int i=1;i<n;i++){
	cin>>x>>y;
	to[x].pb(y),to[y].pb(x);
    }
}

void Upd(int &x,ll y){ x= (ll)x+y>=mod ?(ll)(x+y)%mod :x+y; }

int Min(int a,int b){ return a<b ?a :b; }

int Dp(int u,int fa){
    f[u][0][0]=f[u][1][2]=1;
    int szu=1;
    for(int i=0;i<sz(to[u]);i++){
	int v=to[u][i];
	if(v==fa) continue;
	int szv=Dp(v,u);
	for(int j=0;j<=szu;j++){
	    for(int k=0;k<=szv&&k+j<=K;k++){
		Upd(tmp[j+k][0],(ll)f[u][j][0]*f[v][k][1]);
		
		Upd(tmp[j+k][1],(ll)f[u][j][0]*f[v][k][3]);
		Upd(tmp[j+k][1],(ll)f[u][j][1]*((ll)f[v][k][1]+(ll)f[v][k][3]));

		Upd(tmp[j+k][2],(ll)f[u][j][2]*((ll)f[v][k][0]+(ll)f[v][k][1]));

		Upd(tmp[j+k][3],(ll)f[u][j][2]*((ll)f[v][k][2]+(ll)f[v][k][3]));
		Upd(tmp[j+k][3],(ll)f[u][j][3]*((ll)f[v][k][0]+(ll)f[v][k][1]+(ll)f[v][k][2]+(ll)f[v][k][3]));
	    }
	}
	szu=Min(K,szu+szv);
	for(int j=0;j<=szu;j++)
	    for(int k=0;k<4;k++){
		f[u][j][k]=tmp[j][k];
		tmp[j][k]=0;
	    }
    }
    return szu;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("10.in","r",stdin);
    //freopen("x.out","w",stdout);
#endif
    ios::sync_with_stdio(false);
    Init();
    Dp(1,0);
    cout<<(f[1][K][1]+f[1][K][3])%mod<<endl;
    return 0;
}

以上是关于[JSOI2018]潜入行动 解题报告的主要内容,如果未能解决你的问题,请参考以下文章

JSOI2018 潜入行动

[bzoj5314][Jsoi2018]潜入行动_树形背包dp

[JSOI2018]潜入行动

bzoj 5314: [Jsoi2018]潜入行动

[JSOI2018]潜入行动

[loj2546][JSOI2018]潜入行动(树形DP)