1. 程式人生 > >Luogu4426 HNOI2018/AHOI2018 毒瘤 虛樹

Luogu4426 HNOI2018/AHOI2018 毒瘤 虛樹

傳送門


 

如果原圖是一棵樹的話,很顯然就是統計獨立集數量,直接樹形$DP$就可以了。

但是原圖中又有額外的一些邊,這又要如何去做?

先在圖上摳出一棵樹,發現非樹邊的數量$delta \leq 11$,那麼——我們可以列舉每一條邊的兩個點的選取情況。

考慮到一共有三種情況:$(1,0)(0,1)(0,0)$,但實際上$(0,1)(0,0)$這兩個狀態可以合為一種一起$DP$,所以這裡列舉的複雜度是$2^{delta}$的

然後我們考慮如何在每一種情況下計算答案。如果我們每列舉一次就暴力跑一邊樹形$DP$,複雜度是$O(2^{delta} \times n)$的,似乎是可以拿到$80pts$

如果你能在$HNOI$賽場上拿到$80pts$當然是心滿意足了,但是這裡我們還要進一步優化

發現我們的複雜度瓶頸在樹形$DP$上,因為每一次受影響的最多隻有$2delta$個點,但我們卻對$n$個點都跑了一遍樹形$DP$。

那麼我們可以對這些有非樹邊相連的點構建虛樹,然後把每一條邊在原樹上對應的$DP$係數求出來即可

至於怎麼求這個$DP$係數……先把沒有非樹邊限制的$DP$答案預處理出來,然後直接在原樹上暴跳父親進行$DP$,對於沒有虛點的子樹直接把預處理的答案拿過來。

這樣的複雜度就是$O(2^{delta} \times delta)$的

  1 #include<bits/stdc++.h>
  2
#define int long long 3 //This code is written by Itst 4 using namespace std; 5 6 inline int read(){ 7 int a = 0; 8 bool f = 0; 9 char c = getchar(); 10 while(c != EOF && !isdigit(c)){ 11 if(c == '-') 12 f = 1; 13 c = getchar(); 14 }
15 while(c != EOF && isdigit(c)){ 16 a = (a << 3) + (a << 1) + (c ^ '0'); 17 c = getchar(); 18 } 19 return f ? -a : a; 20 } 21 22 const int MAXN = 100020 , MOD = 998244353; 23 struct Edge{ 24 int end , upEd; 25 }Ed[MAXN << 1] , newEd[50]; 26 struct edge{ 27 int start , end; 28 }link[15]; 29 int jump[MAXN][20] , bef[MAXN][2] , head[MAXN] , newHead[MAXN] , dfn[MAXN] , dep[MAXN] , node[25] , dp[MAXN][2] , s[25] , ori[MAXN][2] , up[MAXN][2][2] , ban[MAXN] , must[MAXN]; 30 int N , cntEd , cntNode , ts , cntNewEd , headS , ans , cntLink; 31 bool in[MAXN]; 32 33 bool operator <(edge a , edge b){ 34 return dfn[a.start] < dfn[b.start]; 35 } 36 37 inline void addEd(Edge* Ed , int* head , int& cntEd , int a , int b){ 38 Ed[++cntEd].end = b; 39 Ed[cntEd].upEd = head[a]; 40 head[a] = cntEd; 41 } 42 43 void init(int now , int fa){ 44 bef[now][0] = bef[now][1] = 1; 45 jump[now][0] = fa; 46 for(int i = 1 ; jump[now][i - 1] ; ++i) 47 jump[now][i] = jump[jump[now][i - 1]][i - 1]; 48 dep[now] = dep[fa] + 1; 49 dfn[now] = ++ts; 50 for(int i = head[now] ; i ; i = Ed[i].upEd) 51 if(Ed[i].end != fa) 52 if(!dep[Ed[i].end]){ 53 init(Ed[i].end , now); 54 bef[now][0] = bef[now][0] * (bef[Ed[i].end][0] + bef[Ed[i].end][1]) % MOD; 55 bef[now][1] = bef[now][1] * bef[Ed[i].end][0] % MOD; 56 in[now] |= in[Ed[i].end]; 57 } 58 else 59 if(dep[Ed[i].end] < dep[now]){ 60 node[++cntNode] = Ed[i].end; 61 node[++cntNode] = now; 62 link[++cntLink].start = Ed[i].end; 63 link[cntLink].end = now; 64 in[now] = 1; 65 } 66 } 67 68 inline int jumpToLCA(int x , int y){ 69 if(dep[x] < dep[y]) 70 swap(x , y); 71 for(int i = 19 ; i >= 0 ; --i) 72 if(dep[x] - (1 << i) >= dep[y]) 73 x = jump[x][i]; 74 if(x == y) 75 return x; 76 for(int i = 19 ; i >= 0 ; --i) 77 if(jump[x][i] != jump[y][i]){ 78 x = jump[x][i]; 79 y = jump[y][i]; 80 } 81 return jump[x][0]; 82 } 83 84 void create(){ 85 for(int i = 1 ; i <= cntNode ; ++i){ 86 int t = jumpToLCA(node[i] , s[headS]); 87 if(t != s[headS]){ 88 while(dep[t] < dep[s[headS - 1]]){ 89 addEd(newEd , newHead , cntNewEd , s[headS - 1] , s[headS]); 90 --headS; 91 } 92 addEd(newEd , newHead , cntNewEd , t , s[headS]); 93 if(s[--headS] != t) 94 s[++headS] = t; 95 } 96 s[++headS] = node[i]; 97 } 98 while(headS - 1 && headS){ 99 addEd(newEd , newHead , cntNewEd , s[headS - 1] , s[headS]); 100 --headS; 101 } 102 if(s[headS] != 1 && headS) 103 addEd(newEd , newHead , cntNewEd , 1 , s[headS]); 104 } 105 106 void init_jump(int now){ 107 ori[now][0] = ori[now][1] = 1; 108 for(int i = head[now] ; i ; i = Ed[i].upEd) 109 if(!in[Ed[i].end]){ 110 ori[now][0] = ori[now][0] * (bef[Ed[i].end][0] + bef[Ed[i].end][1]) % MOD; 111 ori[now][1] = ori[now][1] * bef[Ed[i].end][0] % MOD; 112 } 113 up[now][0][0] = up[now][0][1] = up[now][1][0] = 1; 114 for(int i = jump[now][0] ; i && !newHead[i] ; i = jump[i][0]){ 115 ori[i][0] = ori[i][1] = 1; 116 for(int j = head[i] ; j ; j = Ed[j].upEd) 117 if(!in[Ed[j].end]){ 118 ori[i][0] = ori[i][0] * (bef[Ed[j].end][0] + bef[Ed[j].end][1]) % MOD; 119 ori[i][1] = ori[i][1] * bef[Ed[j].end][0] % MOD; 120 } 121 up[now][0][0] = up[now][0][0] * ori[i][0] % MOD; 122 up[now][0][1] = up[now][0][1] * ori[i][0] % MOD; 123 up[now][1][0] = up[now][1][0] * ori[i][1] % MOD; 124 up[now][1][1] = up[now][1][1] * ori[i][1] % MOD; 125 int p = up[now][0][0] , q = up[now][0][1]; 126 up[now][0][0] = (p + up[now][1][0]) % MOD; 127 up[now][0][1] = (q + up[now][1][1]) % MOD; 128 up[now][1][0] = p; 129 up[now][1][1] = q; 130 } 131 } 132 133 void init_dfs(int now){ 134 for(int i = newHead[now] ; i ; i = newEd[i].upEd) 135 init_dfs(newEd[i].end); 136 init_jump(now); 137 } 138 139 void solve_dp(int now){ 140 dp[now][0] = ori[now][0]; 141 dp[now][1] = ori[now][1]; 142 for(int i = newHead[now] ; i ; i = newEd[i].upEd){ 143 int t = newEd[i].end; 144 solve_dp(t); 145 dp[now][0] = dp[now][0] * ((up[t][0][0] * dp[t][0] + up[t][0][1] * dp[t][1]) % MOD) % MOD; 146 dp[now][1] = dp[now][1] * ((up[t][1][0] * dp[t][0] + up[t][1][1] * dp[t][1]) % MOD) % MOD; 147 } 148 dp[now][0] *= must[now] == 0; 149 dp[now][1] *= ban[now] == 0; 150 } 151 152 void solve_dfs(int now){ 153 if(now > cntLink){ 154 solve_dp(1); 155 ans = (ans + dp[1][0] + dp[1][1]) % MOD; 156 return; 157 } 158 if(!ban[link[now].start]){ 159 ++must[link[now].start]; 160 ++ban[link[now].end]; 161 solve_dfs(now + 1); 162 --must[link[now].start]; 163 --ban[link[now].end]; 164 } 165 if(!must[link[now].start]){ 166 ++ban[link[now].start]; 167 solve_dfs(now + 1); 168 --ban[link[now].start]; 169 } 170 } 171 172 bool cmp(int a , int b){ 173 return dfn[a] < dfn[b]; 174 } 175 176 signed main(){ 177 #ifndef ONLINE_JUDGE 178 freopen("4426.in" , "r" , stdin); 179 freopen("4426.out" , "w" , stdout); 180 #endif 181 N = read(); 182 for(int M = read() ; M ; --M){ 183 int a = read() , b = read(); 184 addEd(Ed , head , cntEd , a , b); 185 addEd(Ed , head , cntEd , b , a); 186 } 187 init(1 , 0); 188 sort(node + 1 , node + cntNode + 1 , cmp); 189 cntNode = unique(node + 1 , node + cntNode + 1) - node - 1; 190 sort(link + 1 , link + cntLink + 1); 191 create(); 192 init_dfs(1); 193 solve_dfs(1); 194 cout << ans; 195 return 0; 196 }