关于 DP 的非常规优化
感觉这个东西就是玄学啊,考场上真的有人能想得出来嘛。(还是我太菜了qwq)
思想现在见到的有这几种:
-
从
推到 时状态改变的数量不会太多,直接继承可以优化。 -
可能对答案有贡献的状态不会太多。即通过一些性质来消除掉冗余状态以保证时间复杂度。
ABC176F Brave CHAIN
容易有简单
-
不换卡:
-
换一张:
-
换两张:
当然有一些类似的不予考虑。我们发现其实没有多少状态被影响到了,若被影响到的多,原因也是
对于第一种,显然对于任意
对于第二种,被影响到的状态只有
对于第三种,若满足
qwq
#include<bits/stdc++.h> using namespace std; const int N = 2000 + 10; int n, f[N][N], arr[N * 3]; void MAX(int& x, int y){if(x < y) x = y;} struct opt{ int k0, k1, val; }; void upd(int a, int b, int w){ MAX(f[a][b], w); MAX(f[a][0], w); MAX(f[0][0], w); } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; memset(f, -0x3f, sizeof f); int allsum = 0; for(int i = 1; i <= 3 * n; i++) cin >> arr[i]; upd(arr[1], arr[2], 0); upd(arr[2], arr[1], 0); for(int i = 1; i < n; i++){ queue<opt> Q; int c = arr[3 * i], d = arr[3 * i + 1], e = arr[3 * i + 2], ad = 0; // f[i][a][b] if(c == d && d == e) ad++, allsum++; // f[i][a][c], f[i][a][d], f[i][a][e] for(int a = 1; a <= n; a++){ if(d == e) Q.push((opt){a, c, f[a][d] + 1}); Q.push((opt){a, c, f[a][0]}); if(c == e) Q.push((opt){a, d, f[a][c] + 1}); Q.push((opt){a, d, f[a][0]}); if(d == c) Q.push((opt){a, e, f[a][d] + 1}); Q.push((opt){a, e, f[a][0]}); } // f[i][c][d], f[i][c][e], f[i][d][e] Q.push((opt){c, d, f[e][e] + 1}); Q.push((opt){c, d, f[0][0]}); Q.push((opt){c, e, f[d][d] + 1}); Q.push((opt){c, e, f[0][0]}); Q.push((opt){d, e, f[c][c] + 1}); Q.push((opt){d, e, f[0][0]}); // update while(!Q.empty()){ opt ths = Q.front(); Q.pop(); int a = ths.k0, b = ths.k1, w = ths.val - ad; upd(a, b, w); upd(b, a, w); } } int ans = 0; for(int a = 1; a <= n; a++){ for(int b = 1; b <= n; b++){ MAX(ans, f[a][b] + (a == b && b == arr[n * 3])); } } cout << ans + allsum; return 0; } /* f[i][a][b] = f[i - 1][a][b] + [c = d = e] f[i][a][c] = f[i - 1][a][b] + [b = d = e] f[i][c][d] = f[i - 1][a][b] + [a = b = e] */
AGC007E Shik and Travel
非常厉害的一道题!
首先容易发现答案具有单调性,于是先通过二分答案
接下来注意到一条边只能走两次,其实就是限制了当进入一棵子树,必须先把里面的所有点经过之后才能离开这棵子树。也就是说,当处理到一颗子树时,需要把它的一颗子树处理完,然后从一个叶子到另一颗子树的叶子,再处理完这个子树,然后离开。
不难发现我们只需要知道两颗子树从
但是想要再优化就没有那么简单了。首先需要注意到一个显然的但是容易被忽略的性质:若
但是这样的时间复杂度?我们设节点
code:
qwq
#include<bits/stdc++.h> #define ll long long #define pir pair<ll, ll> using namespace std; const int N = 2e5 + 10; int n, son[N][3]; ll dis[N][2]; vector<pir> f[N], g[N], vec[N]; ll lim; void clrvec(vector<pir> &v){vector<pir> __qwq; swap(__qwq, v);} void Merge_array(int u){ int j = 0; vector<pir> tmp; for(int i = 0; i < f[u].size(); i++){ while(j < g[u].size() && (g[u][j].first < f[u][i].first || (g[u][j].first == f[u][i].first && g[u][j].second < f[u][i].second))) tmp.push_back(g[u][j++]); tmp.push_back(f[u][i]); } while(j < g[u].size()) tmp.push_back(g[u][j++]); if(tmp.empty()) return; int ttt = 0; vec[u].push_back(tmp[0]); for(int i = 1; i < tmp.size(); i++){ if(tmp[i].second < vec[u][ttt].second) ttt++, vec[u].push_back(tmp[i]); } } void dfs(int u){ if(!son[u][2]){ vec[u].push_back(make_pair(0ll, 0ll)); return;} for(int i = 0; i < 2; i++) dfs(son[u][i]); int ls = son[u][0], rs = son[u][1], j = 0, sum = dis[u][0] + dis[u][1]; for(int i = 0; i < vec[ls].size(); i++){ while(j < vec[rs].size() && vec[ls][i].second + vec[rs][j].first + sum <= lim) j++; if(j) f[u].push_back(make_pair(vec[ls][i].first + dis[u][0], vec[rs][j - 1].second + dis[u][1])); } swap(ls, rs); j = 0; for(int i = 0; i < vec[ls].size(); i++){ while(j < vec[rs].size() && vec[ls][i].second + vec[rs][j].first + sum <= lim) j++; if(j) g[u].push_back(make_pair(vec[ls][i].first + dis[u][1], vec[rs][j - 1].second + dis[u][0])); } Merge_array(u); clrvec(f[u]); clrvec(g[u]); //cout << u << " " << vec[u].size() << " qwq" << "\n"; //for(int i = 0; i < vec[u].size(); i++) cout << vec[u][i].first << " " << vec[u][i].second << "\n"; } bool check(ll V){ //cout << "\n" << V << "\n" << ":::" << "\n"; for(int i = 1; i <= n; i++) clrvec(f[i]), clrvec(g[i]), clrvec(vec[i]); lim = V; dfs(1); return vec[1].size(); } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; for(int i = 2; i <= n; i++){ int x, y; cin >> x >> y; son[x][son[x][2]] = i; dis[x][son[x][2]] = y; ++son[x][2]; } ll l = 0, r = 3e10, ans = 3e10; while(l <= r){ ll mid = (l + r >> 1); if(check(mid)) r = mid - 1, ans = mid; else l = mid + 1; } cout << ans; return 0; }
也是学会使用
本文作者:little-corn
本文链接:https://www.cnblogs.com/little-corn/p/18537382
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步