题目:Economic Difficulties
传送门:https://codeforces.com/contest/1263/problem/F
题意:给了两棵tree:Ta(拥有a个节点,节点编号为[n+1, n+a]) Tb(拥有b个节点, 节点编号: [n+ a + 1, n + a + b]) 其中两颗tree的叶子节点按照dfs序依次连续连向 $ i, i in [1, n] $节点,询问最多可以去掉多少的tree上节点,使得每一个$i , i in [1, n]$ 仍然可以连通Ta,或者Tb的根节点。
分析:
1.做法一:https://codeforces.com/blog/entry/71844?locale=en
类似泛化背包,每个物品要么选择a,要么选择b,连续选择有额外贡献,假设cost_a[i,j]代表[i,j]区间选择a的贡献,cost_b[i,j]代表[i,j]区间选择b的贡献。
dp[i]代表前i个节点,方程:$$ dp[i] = max{dp[j] + cost[j+1,i] }$$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 vector< vector<int> > calc_cost(int vn, int n, vector<int> fa, vector<int> tg) { 5 vector< vector<int> > res(n+1, vector<int>(n+1)); 6 for (int l = 1; l <= n; l++) { 7 vector<int> deg(vn+1); 8 for (int i = 2; i <= vn ; i++) deg[fa[i]]++; 9 int val = 0; 10 for (int r = l; r <= n; r++) { 11 for(int v = tg[r]; v != 1 && deg[v] == 0; --deg[v = fa[v]]) 12 val++; 13 res[l][r] = val; 14 } 15 } 16 return res; 17 } 18 19 int main() { 20 int n; 21 scanf("%d", &n); 22 vector< vector<int> > cost[2]; 23 for (int j = 0; j < 2; j++) { 24 int vn; 25 scanf("%d", &vn); 26 vector<int> fa(vn+1); 27 vector<int> tg(n+1); 28 for (int i = 2; i <= vn; i++) scanf("%d", &fa[i]); 29 for (int i = 1; i <= n; i++) scanf("%d", &tg[i]); 30 cost[j] = calc_cost(vn, n, fa, tg); 31 } 32 33 vector<int> dp(n + 1, 0); 34 for (int i = 1; i <= n; i++) 35 for (int j = 0; j < i; j++) 36 dp[i] = max(dp[i], dp[j] + max(cost[0][j+1][i], cost[1][j+1][i])); 37 printf("%d", dp[n]); 38 return 0; 39 }
2.基于最后的结果,我们肯定是选择Ta和Tb的一些节点,去除这些节点及其后代节点,考虑到这里,我们只要把每个节点控制的区间算出来,然后按照左端点或者有端点排序,然后做限制背包dp。
#include <bits/stdc++.h> using namespace std; const int MAXN = 1e4 + 7; const int INF = 1e9 + 7; vector< pair<pair<int,int>, int> > vec; int sz[MAXN], st[MAXN], ed[MAXN], tg[MAXN]; vector<int> G[MAXN]; void dfs(int u) { sz[u] = 1; st[u] = INF; ed[u] = -INF; if(tg[u]) st[u] = ed[u] = tg[u]; for(auto v : G[u]) { dfs(v); sz[u] += sz[v]; st[u] = min(st[u], st[v]); ed[u] = max(ed[u], ed[v]); } vec.push_back(make_pair(make_pair(ed[u], st[u] - 1), sz[u]-(u == 1))); } int dp[MAXN]; int main() { int n, a, x; scanf("%d", &n); for(int j = 0; j < 2; j++) { scanf("%d", &a); for(int i = 2;i <= a; i++) { scanf("%d", &x); G[x].push_back(i); } for(int i = 1; i <= n; i++) { scanf("%d", &x); tg[x] = i; } dfs(1); for(int i = 1; i <= a; i++) { G[i].clear(); tg[i] = 0; } } sort(vec.begin(), vec.end()); for(auto it : vec) { int l = it.first.second, r = it.first.first; dp[r] = max(dp[r], dp[l] + it.second); } printf("%d", dp[n]); return 0; }
3.和文理分科类似,该题也可以用网络流求解,然而我到现在都不知道为啥是对的。
#include <bits/stdc++.h> using namespace std; typedef int LL; const int MAXN = 1e5 + 7; const int MAXM = 1e5 + 7; const int INF = 1e9 + 7; namespace NWF { struct Edge { int to, nxt;LL f; } e[MAXM << 1]; int S, T, tot; int ecnt, head[MAXN], cur[MAXN], dis[MAXN]; queue<int> q; void init(int _S, int _T, int _tot){ ecnt = 1; S = _S; T = _T; tot = _tot; memset(head, 0, (tot + 1) * sizeof(int)); } void addEdge(int u, int v, LL f) { e[++ecnt] = (Edge) {v, head[u], f}; head[u] = ecnt; e[++ecnt] = (Edge) {u, head[v], 0}; head[v] = ecnt; } bool bfs() { memset(dis, 0, (tot + 1) * sizeof(int)); q.push(S); dis[S] = 1; while (!q.empty()) { int u = q.front(), v; q.pop(); for (int i = cur[u] = head[u]; i ; i = e[i].nxt) { if (e[i].f && !dis[v = e[i].to]) { q.push(v); dis[v] = dis[u] + 1; } } } return dis[T]; } LL dfs(int u, LL maxf) { if (u == T) return maxf; LL sumf = maxf; for (int &i = cur[u]; i; i = e[i].nxt) { if (e[i].f && dis[e[i].to] > dis[u]) { LL tmpf = dfs(e[i].to, min(sumf, e[i].f)); e[i].f -= tmpf; e[i ^ 1].f += tmpf; sumf -= tmpf; if (!sumf) return maxf; } } return maxf - sumf; } LL dinic() { LL ret = 0; while (bfs()) ret += dfs(S, INF); return ret; } } int faa[MAXN], fab[MAXN], tga[MAXN], tgb[MAXN]; int main() { int n, A, B; scanf("%d", &n); scanf("%d", &A); for(int i = 2;i <= A; i++) scanf("%d", faa+i); for(int i = 1; i <= n; i++) scanf("%d", tga + i); scanf("%d", &B); for(int i = 2;i <= B; i++) scanf("%d", fab+i); for(int i = 1; i <= n; i++) scanf("%d", tgb + i); NWF::init(A+B+1,A+B+2, A+B+3); for (int i = 2; i <= A; i++) { NWF::addEdge(NWF::S, i, 1); NWF::addEdge(faa[i], i, INF); } for (int i = 2; i <= B; i++) { NWF::addEdge(A + i, A + fab[i], INF); NWF::addEdge(A + i, NWF::T, 1); } for (int i = 1; i <= n; i++) NWF::addEdge(tga[i], A + tgb[i], INF); int ans = A - 1 + B - 1 - NWF::dinic(); printf("%d", ans); return 0; }
#include <bits/stdc++.h> using namespace std; typedef int LL; const int MAXN = 1e5 + 7; const int MAXM = 1e5 + 7; const int INF = 1e9 + 7; namespace NWF { struct Edge{ int to, nxt;LL f; }e[MAXM << 1]; int S, T, tot; int ecnt, head[MAXN], cur[MAXN], pre[MAXN], num[MAXN], dis[MAXN]; queue<int> q; void init(int _S, int _T, int _tot){ ecnt = 1; S = _S; T = _T; tot = _tot; memset(num, 0, (tot + 1) * sizeof(int)); memset(head, 0, (tot + 1) * sizeof(int)); } inline void addEdge(int u, int v, LL f) { e[++ecnt] = (Edge) {v, head[u], f}; head[u] = ecnt; e[++ecnt] = (Edge) {u, head[v], 0}; head[v] = ecnt; } void bfs() { memset(dis, 0, (tot + 1) * sizeof(int)); q.push(T); dis[T] = 1; while(!q.empty()) { int u = q.front(), v; q.pop(); num[dis[u]]++; for(int i = cur[u] = head[u]; i; i = e[i].nxt) { if(!dis[v = e[i].to]) { dis[v] = dis[u] + 1; q.push(v); } } } } LL augment() { LL flow = INF; for(int i = S; i != T; i = e[cur[i]].to) flow = min(flow, e[cur[i]].f); for(int i = S; i != T; i = e[cur[i]].to) { e[cur[i]].f -= flow; e[cur[i] ^ 1].f += flow; } return flow; } LL isap() { bfs(); int u = S, v; LL flow = 0; while(dis[S] <= tot) { if(u == T) { flow += augment(); u = S; } bool fg = 0; for(int i = cur[u]; i; i = e[i].nxt) { if(e[i].f && dis[u] > dis[v = e[i].to]) { pre[v] = u; cur[u] = i; u = v; fg = 1; break; } } if(fg) continue; if(!--num[dis[u]]) break; int maxDis = tot; for(int i = head[u]; i; i = e[i].nxt) { if(e[i].f && maxDis > dis[v = e[i].to]) { maxDis = dis[v]; cur[u] = i; } } num[dis[u] = maxDis + 1]++; if(u != S) u = pre[u]; } return flow; } } int faa[MAXN], fab[MAXN], tga[MAXN], tgb[MAXN]; int main() { int n, A, B; scanf("%d", &n); scanf("%d", &A); for(int i = 2;i <= A; i++) scanf("%d", faa+i); for(int i = 1; i <= n; i++) scanf("%d", tga + i); scanf("%d", &B); for(int i = 2;i <= B; i++) scanf("%d", fab+i); for(int i = 1; i <= n; i++) scanf("%d", tgb + i); NWF::init(A+B+1,A+B+2, A+B+3); for (int i = 2; i <= A; i++) { NWF::addEdge(NWF::S, i, 1); NWF::addEdge(faa[i], i, INF); } for (int i = 2; i <= B; i++) { NWF::addEdge(A + i, A + fab[i], INF); NWF::addEdge(A + i, NWF::T, 1); } for (int i = 1; i <= n; i++) NWF::addEdge(tga[i], A + tgb[i], INF); int ans = A - 1 + B - 1 - NWF::isap(); printf("%d", ans); return 0; }