CCPC 2016 杭州 E. Master of Subgraph(点分治+bitset优化DP)
题目链接:http://acm.hdu.edu.cn/downloads/CCPC2018-Hangzhou-ProblemSet.pdf
题意:给定一棵有 n 个结点的树和一个数 m,对于 i ∈ [1,m] 问是否存在一个子图结点的权值和为 i 。
题解:一个显然的思路是树上做背包,但显然会 T。要遍历全部子图,考虑进行点分治,然后合并的时候用 bitset 优化背包,时间复杂度O(nmlogn / 64),且时限给了 8s。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define ull unsigned long long 5 #define mst(a,b) memset((a),(b),sizeof(a)) 6 #define mp(a,b) make_pair(a,b) 7 #define pi acos(-1) 8 #define pii pair<int,int> 9 #define pb push_back 10 const int INF = 0x3f3f3f3f; 11 const double eps = 1e-6; 12 const int maxn = 3e3 + 10; 13 const int maxm = 1e5 + 10; 14 const ll mod = 998244353; 15 16 int n,m; 17 vector<int>vec[maxn]; 18 bool used[maxn]; 19 int a[maxn],root,sz[maxn],son[maxn],all; 20 21 void getroot(int u,int fa) { 22 sz[u] = 1, son[u] = 0; 23 for(int i = 0; i < vec[u].size(); i++) { 24 int v = vec[u][i]; 25 if(used[v] || v == fa) continue; 26 getroot(v,u); 27 sz[u] += sz[v]; 28 son[u] = max(son[u],sz[v]); 29 } 30 son[u] = max(son[u],all - son[u]); 31 if(son[u] < son[root]) root = u; 32 } 33 34 bitset<maxm>bit[maxn],ans; 35 36 void calc(int u,int fa) { 37 sz[u] = 1, bit[u] <<= a[u]; 38 for(int i = 0; i < vec[u].size(); i++) { 39 int v = vec[u][i]; 40 if(used[v] || v == fa) continue; 41 bit[v] = bit[u]; 42 calc(v,u); 43 sz[u] += sz[v]; 44 bit[u] |= bit[v]; 45 } 46 } 47 48 void solve(int u) { 49 used[u] = true; 50 bit[u].reset(), bit[u].set(0); 51 calc(u,0); 52 ans |= bit[u]; 53 for(int i = 0; i < vec[u].size(); i++) { 54 int v = vec[u][i]; 55 if(used[v]) continue; 56 root = 0; 57 all = sz[v]; 58 getroot(v,0); 59 solve(root); 60 } 61 } 62 63 int main() { 64 #ifdef local 65 freopen("data.txt", "r", stdin); 66 // freopen("data.txt", "w", stdout); 67 #endif 68 int t; 69 scanf("%d",&t); 70 while(t--) { 71 ans.reset(); 72 scanf("%d%d",&n,&m); 73 for(int i = 0; i <= n; i++) vec[i].clear(),used[i] = false; 74 for(int i = 1; i < n; i++) { 75 int u,v; 76 scanf("%d%d",&u,&v); 77 vec[u].push_back(v); 78 vec[v].push_back(u); 79 } 80 for(int i = 1; i <= n; i++) scanf("%d",&a[i]); 81 root = 0; 82 son[0] = 1e9; 83 all = n; 84 getroot(1,0); 85 solve(root); 86 for(int i = 1; i <= m; i++) printf("%d",(int)ans[i]); 87 printf("\n"); 88 } 89 return 0; 90 }