天天看點

Codeforces 1332 F Independent Set ——樹形DP

題意:

給你一棵樹,問你它的所有邊子集的所有點獨立集的情況數是多少。

邊子集就是邊的子集且任何一個存在的點都有至少一條邊連着。點獨立集就是任意兩個點在圖中沒有邊連着。

題解:

這個邊子集搞的我不知所措,一直沒看懂。。那麼就是目前點如果取了,它的所有兒子都不能取,它如果不取的話,所有兒子可取可不取。但是還有一種情況:它不和這個兒子相連,那麼用dp[i][2]表示。

那麼狀态轉移方程就是:

sum=(dp[ne][0]+dp[ne][1]-dp[ne][2]+mod)%mod;
        dp[x][0]=dp[x][0]*(dp[ne][0]+dp[ne][1]+sum)%mod;
        dp[x][1]=dp[x][1]*(dp[ne][0]+sum)%mod;
        dp[x][2]=dp[x][2]*sum%mod;
           

就是乘法原理。為什麼要減去dp[ne][2],因為無論這個點取0還是1,如果它不與兒子相連,都無所謂,也就是多算了一次。

最後減去空集的情況

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
#define ll long long
const ll mod=998244353;
ll dp[N][3];
struct node{
    int to,next;
}e[N*2];
int cnt,head[N];
void add(int x,int y){
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
ll ans;
void dfs(int x,int fa){
    dp[x][0]=dp[x][1]=dp[x][2]=1;
    ll sum=0;
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(ne==fa)continue;
        dfs(ne,x);
        sum=(dp[ne][0]+dp[ne][1]-dp[ne][2]+mod)%mod;
        dp[x][0]=dp[x][0]*(dp[ne][0]+dp[ne][1]+sum)%mod;
        dp[x][1]=dp[x][1]*(dp[ne][0]+sum)%mod;
        dp[x][2]=dp[x][2]*sum%mod;
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,0);
    printf("%lld\n",(dp[1][0]+dp[1][1]-dp[1][2]-1+mod)%mod);
    return 0;
}