codeforces 1332 F. Independent Set(树形dp)

题目链接:https://codeforc.es/contest/1332/problem/F

思路:这题选的是除了空集之外边的子集,一开始做了个联通子图的,自闭了好久。这题可以先考虑选的边是全集怎么做,边是全集很明显可以用dp[u][1]表示这个点染色了的独立集数目,dp[u][0]表示这个点没染色的独立集数目。转移方程也很显然,如果当前点染色了,那么它的儿子必须是没染色的,否则它的儿子可以是任何颜色。转移方程也很显然

$$dp[u][0]=\prod (dp[v][1]+dp[v][0])$$

$$dp[u][1]=\prod dp[v][0]$$

对于这个题只要加上一个状态dp[u][2]表示u点和它每个儿子都断开的方案数就行了,用qw[v]表示父节点与v断开,v子树的所有方案数,对于dp[u][1]和dp[u][0]来说如果u-v这条边被删掉了,那么v这颗子树怎么选都是可以的,所以转移时需要加上qw[v]的情况,而对于dp[u][2]来说由于每个儿子都断开了,那么它的方案数就是儿子节点qw[v]的乘积,需要这个状态的原因是由于和儿子都断开后,这个点变成了一个独立的点,那么其实它怎么染色都无所谓,但dp[v][0]+dp[v][1]重复计算了这种情况,在计算qw[v]的时候需要把这种重复情况减去

qw[v]=dp[v][1]+dp[v][0]-dp[v][2]

dp[u][0]=\prod dp[v][1]+dp[v][0]+qw[v]

$$dp[u][1]=\prod dp[v][0]+qw[v]$$

$$dp[u][2]=\prod qw[v]$$

需要注意的是最后的答案需要减去空集的情况

#pragma GCC optimize(2)
#include 
#include 
using namespace __gnu_pbds;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll inff = 0x3f3f3f3f3f3f3f3f;
#define FOR(i,a,b) for(int i(a);i<=(b);++i)
#define FOL(i,a,b) for(int i(a);i>=(b);--i)
#define REW(a,b) memset(a,b,sizeof(a))
#define inf int(0x3f3f3f3f)
#define si(a) scanf("%d",&a)
#define sl(a) scanf("%I64d",&a)
#define sd(a) scanf("%lf",&a)
#define ss(a) scanf("%s",a)
#define mod ll(998244353)
#define pb push_back
#define eps 1e-6
#define lc d<<1
#define rc d<<1|1
#define Pll pair
#define P pair
#define pi acos(-1)
#define VI vector>
using namespace std;
const int N=3e5+8;
ll n,x,y,dp[N][3],ans;
vectorg[N];

void dfs(int u,int fa)
{
    dp[u][0]=dp[u][1]=dp[u][2]=1;
    for(int i=0;i>n;
    FOR(i,1,n-1)
    {
        sl(x),sl(y);
        g[x].pb(y);
        g[y].pb(x);
    }
    dfs(1,0);
    cout<<(dp[1][1]+dp[1][0]-dp[1][2]-1+mod)%mod<

 

你可能感兴趣的:(dp,动态规划,dfs)