1. 程式人生 > >luogu P4426 [HNOI/AHOI2018]毒瘤 虛樹dp

luogu P4426 [HNOI/AHOI2018]毒瘤 虛樹dp

題意

  • 給你一個連通圖,求獨立集個數,滿足 m < = n + 10 m<=n+10

這個題真的跟題目描述一樣毒瘤,網上的題解看了好久都看不懂,只能結合別人的程式碼邊抄邊想了。

首先在考場上有一個很優秀的暴力,我們把非樹邊提出來,暴力列舉非樹邊的選取狀態,可以做到 O ( 3 m

n + 1 n ) O(3^{m-n+1}n) 的複雜度,我們考慮優化這個暴力,可以發現,每次做 O
( n ) O(n)
的樹形 d p dp 的時候,很多點的轉移都是相同的,我們把所有非樹邊上的點定義為關鍵點,那麼我們建出所有關鍵點的虛樹之後,對於虛樹上的兩個點 x , y ( f a [ y ] = x ) x,y(fa[y]=x) ,轉移可以寫成這樣的形式。
f [ x ] [ 1 ] = f [ x ] [ 1 ] × ( k 1 × f [ y ] [ 0 ] + k 2 × f [ y ] [ 1 ] ) f[x][1]=f[x][1]×(k1×f[y][0]+k2×f[y][1])
f [ x ] [ 0 ] = f [ x ] [ 0 ] × ( k 3 × f [ y ] [ 0 ] + k 4 × f [ y ] [ 1 ] ) f[x][0]=f[x][0]×(k3×f[y][0]+k4×f[y][1])
那麼現在我們要做的就是怎樣計算 k k ,這個東西實際上我們進行一次 O ( n ) O(n) d p dp 就可以計算出來,設 k [ x ] [ 0 / 1 ] [ 0 / 1 ] k[x][0/1][0/1] 表示虛樹上 x x 這個點,第一個 [ 0 / 1 ] [0/1] 表示它自己選不選,第二個表示它要轉移到的那個點選不選,那麼我們每次提出虛樹中兩個點之間的鏈進行 d p dp 即可, d p dp 的轉移和暴力是一樣的,這裡就不寫了。還有 f f 值就是表示刪去虛樹兒子那些鏈後的值, d p dp 過的點也一定要標記,否則會算重。那我們按照原來那個方法在虛樹上 d p dp 可以做到 O ( n + ( m n + 1 ) 3 m n + 1 ) O(n+(m-n+1)3^{m-n+1}) 複雜度,然後就做完了。

總結一下,首先建出虛樹,然後對於虛樹上每個點求出它往它父親的轉移係數,處理的時候要對於虛樹上的點要不包含虛樹上的兒子的 d p dp 值,推出轉移係數之後直接暴力列舉加 d p dp 就可以了

Codes

#include <bits/stdc++.h>

#define pb push_back
#define mp make_pair
#define x first
#define y second

using namespace std;

const int M = 10 + 8;
const int N = 1e5 + 10;
const int mod = 998244353;

vector<int> G[N];
pair<int, int> E[N];
map<int, bool> cut[N];

int to[N << 1], nxt[N << 1], head[N], e; 
int fa[N][M], dep[N], st[N], ed[N], rel[N << 1], ban[N]; 
int tmp[N], ins[N], FA[N], f[N][2], k[N][2][2], dp[N][2];
int n, m, cnt, tot, dfn_cnt;

void add(int x, int y) {
    if (!x || !y) return;
    to[++ e] = y; nxt[e] = head[x]; head[x] = e; 
}

int lca(int x, int y) {
    if (dep[x] ^ dep[y]) {
        if (dep[x] < dep[y]) swap(x, y);
        for (int j = M - 1; ~j; -- j) 
            if (dep[fa[x][j]] > dep[y])
                x = fa[x][j];
        x = fa[x][0];
    }
    if (x ^ y) {
        for (int j = M - 1; ~j; -- j) 
            if (fa[x][j] ^ fa[y][j])
                x = fa[x][j], y = fa[y][j];
        x = fa[x][0];
    }
    return x; 
}

void dfs(int x, int dad) {
    fa[x][0] = dad, rel[st[x] = ++ dfn_cnt] = x; 
    for (int j = 1; j < M; ++ j) 
        fa[x][j] = fa[fa[x][j - 1]][j - 1];
    for (auto v : G[x]) if (v ^ dad) {
        if (!st[v]) dep[v] = dep[x] + 1, dfs(v, x);
        else if (st[v] < st[x]) 
            E[++ cnt] = mp(x, v), cut[x][v] = cut[v][x] = 1;
    }
    rel[ed[x] = ++ dfn_cnt] = x; 
}

void DP(int x, int no) {
    f[x][0] = f[x][1] = 1, ins[x] = 1; 
    for (auto v : G[x]) if (v ^ fa[x][0] && v ^ no && !cut[x][v] && !ins[v]) {
        DP(v, no), f[x][1] = 1ll * f[x][1] * f[v][0] % mod;
        f[x][0] = 1ll * f[x][0] * (f[v][1] + f[v][0]) % mod; 
    }
}

void get_k(int x, int y) {
    // k[x][0][0] x no choose i no choose
    // k[x][0][1] x no choose i choose
    // k[x][1][0] x choose i no choose
    // k[x][1][1] x choose i choose
    k[x][0][0] = k[x][1][1] = 1; 
    for (int i = x; fa[i][0] ^ y; i = fa[i][0]) {
        int tp[2][2]; DP(fa[i][0], i);
        tp[0][0] = 1ll * f[fa[i][0]][0] * (k[x][0][1] + k[x][0][0]) % mod;
        tp[1][0] = 1ll * f[fa[i][0]][0] * (k[x][1][0] + k[x][1][1]) % mod; 
        tp[0][1] = 1ll * f[fa[i][0]][1] * k[x][0][0] % mod; 
        tp[1][1] = 1ll * f[fa[i][0]][1] * k[x][1][0] % mod; 
        swap(k[x], tp);
    }
}

void DFS(int x) {
    for (int i = head[x]; i; i = nxt[i]) DFS(to[i]), get_k(to[i], x);
    f[x][0] = f[x][1] = 1; 
    for (auto v : G[x]) if (!cut[x][v] && v ^ fa[x][0] && !ins[v]) {
        DP(v, 0), f[x][1] = 1ll * f[x][1] * f[v][0] % mod; 
        f[x][0] = 1ll * f[x][0] * (f[v][1] + f[v][0]) % mod;
    }
}

void build() {
    stack<int> S; tmp[tot = 1] = 1; 
    for (int i = 1; i <= cnt; ++ i) 
        tmp[++ tot] = st[E[i].x], tmp[++ tot] = st[E[i].y];
    sort(tmp 
            
           

相關推薦

luogu P4426 [HNOI/AHOI2018]毒瘤 dp

題意 給你一個連通圖,求獨立集個數,滿足 m &lt; =

Luogu4426 HNOI2018/AHOI2018 毒瘤

傳送門   如果原圖是一棵樹的話,很顯然就是統計獨立集數量,直接樹形$DP$就可以了。 但是原圖中又有額外的一些邊,這又要如何去做? 先在圖上摳出一棵樹,發現非樹邊的數量$delta \leq 11$,那麼——我們可以列舉每一條邊的兩個點的選取情況。 考慮到一共有三種情況:$(1,0)

Luogu P4425 [HNOI/AHOI2018]轉盤 線段

題意 一個轉盤上有 n n n個物品,每個物品有一個出現時間,你每次

[BZOJ5287][HNOI2018]毒瘤(DP)

暴力列舉非樹邊取值做DP可得75。 注意到每次枚舉出一個容斥狀態的時候,都要做大量重複操作。 建立虛樹,預處理出虛樹上兩點間的轉移係數。也可動態DP解決。 樹上倍增、動態DP、虛樹DP似乎是這種問題的三種通用解法。 程式碼不是特別長但極其難寫,預處理過程中要考慮各種情況。水平不夠只好抄程式碼。

luogu P4438 [HNOI/AHOI2018]道路 樹形dp

傳送門 講一下做題的過程  Day1 指定標籤搜尋 看題 從入門到入土 噁心 再見吧 Day2 這麼咕了一道題好像不好?還是做一下吧 10 minutes later…… 這不就是個二叉樹嗎! 題意就是統計每個點經過的左右邊的路徑上選擇多少條邊進行標記 然後化成一個沒法巧算

[BZOJ3572][HNOI2014]世界(DP)

iostream ash char ios swa break sort 之前 題目 代碼用時1:15 思想比較簡單的虛樹DP,但細節巨茍,大部分代碼都是LCA/DP/虛樹模板,真正需要自己寫的其實並不多。 寫之前要有一個清晰的思路和框架,細節要有一個比較清楚的認識,不能依

【Bzoj2286】消耗戰(+DP

swap mem cpp php down oid info LV algo Description 題目鏈接 Solution 在虛樹上跑DP即可 Code #include <cstdio> #include <algorithm> #inclu

#10 //I [HNOI/AHOI2018]毒瘤

div 我們 ++ ack cin 一個點 mem 簡單的 dfs 題解: 80分做法還是聽簡單的 對於非樹邊枚舉一下端點狀態 然而我也不知道為什麽就多t了一個點 具體實現上 最暴力的是3^n次 但是我們可以發現對於i不取,j取 i不取,j不取是可以等效成i不取,j沒有限制

CodeForces - 613D:Kingdom and its Cities(+DP

for pro void 染色 however == force don tac Meanwhile, the kingdom of K is getting ready for the marriage of the King‘s daughter. However

HDU-6035:Colorful Tree(+DP

node different ase 得到 第一題 false all 直接 files 這裏有三道長得像的題: 一: HDU6036: There is a tree with nn nodes, eac

[luogu]P4437 [HNOI/AHOI2018]排列

hnoi clas 個數 新的 http 最大 pro spa www 原題鏈接 :P4437 [HNOI/AHOI2018]排列 分析 給定一個序列,第i項有\(a_i,w_i\)兩個屬性。 現在給序列重排列,保持標號不變,要求在新的序列中,第i個數後面不存在第j個數使\

洛谷4103 HEOI2014大工程(+dp

題目連結 又是一道虛樹好題啊 我們建出來虛樹,然後考慮dp過程,我們分別令 s u m

洛谷3233 HNOI2014(+dp

題目連結 膜拜一發 m t s _

「WC2018」通道-邊分治++DP

Description 給定三棵樹,最大化 d i s

洛谷 P3233 [HNOI2014]世界(+dp)

題面 luogu 題解 資料範圍已經告訴我們是虛樹了,考慮如何在虛樹上面\(dp\) 以下摘自hzwer部落格: 構建虛樹以後兩遍dp處理出虛樹上每個點最近的議事處 然後列舉虛樹上每一條邊,考慮其對兩端點的答案貢獻 可以用倍增二分出分界點 如果a,b的分界點為mid,a,b路徑上a的第一個兒子

BZOJ5287 HNOI2018毒瘤+樹形dp

  顯然的做法是暴力列舉非樹邊所連線兩點的選或不選,大力dp。考場上寫的是最暴力的O(3n-mn),成功比大眾分少10分。容斥或者注意到某些列舉是不必要的就能讓底數變成2。但暴力的極限也就到此為止。   每次重新dp做了大量重複的事,考慮從減少重複計算方面優化。先跑一遍沒有限制的樹形dp。將非樹邊所連線的點

[BZOJ5287][Hnoi2018]毒瘤 + 樹形 DP

Address 洛谷 P4426 BZOJ 5287 LOJ #2496 Solution - Step 1 首先我們看到非樹邊最多 11

【CF809E】Surprise me! 樹形DP 數學

sum .com body surprise algorithm rac com rap can 題目大意   給你一棵\(n\)個點的樹,每個點有權值\(a_i\),\(a\)為一個排列,求 \[ \frac{1}{n(n-1)}\sum_{i=1}^n\sum_{j=1

luogu P3174 [HAOI2009] 毛毛蟲 dp

傳送門 樹形dp基礎題 求帶點權樹直徑 然後這個點權就是每個點的點度(son[])... 所以可以簡化一下 按照正常的套路維護從根開始的最長鏈和次長鏈 dp陣列存的時候+1改成+son[x]-1就可以了 所以dp[x] = maxx + maxxx + son[x] - 1 (maxx 和 ma

luogu P2607 [ZJOI2008] 騎士 dp

傳送門 又一個沒有上司的舞會 這個dp有環 媽媽怎麼辦啊 要不...環上隨便斷一條邊? 然後最後選的時候分別取兩個根節點不選的情況的最大值 幾個要點: 1.圖可能是多個環套樹 要迴圈走完 2.不能只記錄頂點 因為如果有重邊的話會把二元環篩掉 3.位運算優先順序..