2018 ICPC Asia Jakarta Regional Contest(补题中)

加粗:赛时AC
普通:赛后AC
我是眼缘,破DP调个半年调不出来,被杰哥重打一遍过了,全程就只出了一题被队友带飞。
A. Edit Distance
题目给的要求很松散,构造的方式有很多。对于0比1多的串我们只要输出一个全是1的串就行,那么我们最少要出现 $ \frac n 2 $以上个0,最少要操作$ \frac n 2 $以上次,1比0多同理。
当0和1相等的时候,我们考察第一个数字,是0输出10000…否则输出0111…
拓展:这题的条件限制的太宽了,最小编辑距离是一种非常典型的DP类问题,思考:假如我们要求构造的序列修改次数最大而不是大于n/2,最后能不能做。我觉得是可以的,感觉这题原本就是这样出的,最后被砍成了签到题,和队友讨论一下然后逮捕。
D. Icy Land
一开始没听懂题目。。。
当行和列都大于等于3的时候,中间的每一个都要变成干燥地面,然后外面一圈要除了四角外有一个干燥地面能够进入中间部分。
当行数小于3的时候,要求是除了第一列和最后一列以外,每一列都至少有一个干燥地面,列数小于3的时候也是一样的道理。
当两者都小于3,答案是0.
G. Go Make It Complete
壕哥出的,我一开始想的是二分之类的。
比较暴力的方式是将每一个未连接点对扔进优先队列里,然后从大到小连边,每连一对边就将后面含有这两个点的点对度数+1.记录最小的度数,这种方式复杂度很高。
我们依然从大到小连接,每连接一对点,最多有n-2个其他的点受到影响,对于每个实际受影响的点,如果连接之后度数大于当前记录的答案,那么我们可以直接连接这条边,然后用队列维护被更新的点。用这些点去更新其他的点,那么就可以保证对于每次更新,队列里含有点数小于n个点。如果连接之后小于当前记录的答案,我们就不更新扔回进优先队列里,后续一定是会比队列里更新前的数据优先处理。队列的复杂度上限是O(n3)相当于每个点都做一次,优先队列上限是O(n2logn)相当于每个点对都做一次。

#include <iostream> #include <cstdio> #include <algorithm> #include <queue> using namespace std; int v[510][510],v1[510],du[510]; struct node { int sum; int i,j; friend bool operator <(node a,node b) { return a.sum<b.sum; } }; priority_queue <node> q; int main() { int n,m,i,j; scanf("%d%d",&n,&m); for (i=1; i<=m; i++) { int x,y; scanf("%d%d",&x,&y); du[x]++,du[y]++; v[x][y]=v[y][x]=1; } for (i=1; i<=n; i++) for (j=i+1; j<=n; j++) if (!v[i][j]) { node now; now.sum=du[i]+du[j]; now.i=i,now.j=j; q.push(now); } int ans=1e9; while(!q.empty()) { node now=q.top(); q.pop(); if(v[now.i][now.j]) continue; ans=min(ans,du[now.i]+du[now.j]); v[now.i][now.j]=v[now.j][now.i]=1; du[now.i]++,du[now.j]++; queue<int>q1; q1.push(now.i),q1.push(now.j); v1[now.i]=v1[now.j]=1; //printf("%d %d\n",now.i,now.j); while(!q1.empty()) { int i=q1.front(); // printf("%d\n",i); q1.pop(); v1[i]=0; for(j=1; j<=n; j++) { if (i==j||v[i][j]) continue; if(du[i]+du[j]<ans) { node now; now.sum=du[i]+du[j]; now.i=i,now.j=j; q.push(now); } else { v[i][j]=v[j][i]=1; du[i]++,du[j]++; if(!v1[i]) q1.push(i),v1[i]=1; if(!v1[j]) q1.push(j),v1[j]=1; } } } } printf("%d\n",ans); return 0; }
I. Lie Detector
签到,跟着题意做就行了。
J. Future Generation
有人血c,有人吸血。
我废了,两个小时调不出来简单dp,最后杰哥重打一遍直接过了。。。
我们预处理出每一个字符串的所有子序列,然后直接对串进行按照字典序排序,dp的时候,对每一个原串它前一个串的子序列串进行二分查找,找到的位置之前的(杰哥是倒过来做的,所以他的代码是之后)所有串都是可以被选中的,所以处理处上一层的最大值,然后用最大值加上当前串的长度即可。

#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <array> #include <vector> #include <map> #include <unordered_map> #include <set> #include <unordered_set> #include <stack> #include <queue> #include <bitset> #include <algorithm> #include <numeric> #include <functional> #include <cmath> using namespace std; typedef long long ll; #define finc(i,a,b) for(int i=(int)(a);i<(int)(b);i++) #define fdec(i,a,b) for(int i=(int)(b);i-->(int)(a);) #define reset(a,...) a=decltype(a)(__VA_ARGS__) #define endl '\n' #define endb ' ' #define read(a,str) scanf("%"#str,&a) #define print(a,str) printf("%"#str,a) int n, m; vector<string> inp; vector<vector<string>> sub; vector<vector<int>> dp; void f(int now) { int len = inp[now].size(); finc(i, 1, 1 << len) { string str; finc(j, 0, len) if (1 << j & i) str += inp[now][j]; sub[now].push_back(str); } } void ac() { cin >> n; m = 1 << n; reset(inp, n + 1), reset(sub, n + 1), reset(dp, n + 1); finc(i, 1, n + 1) cin >> inp[i]; finc(i, 1, n + 1) f(i), sort(sub[i].begin(), sub[i].end()); finc(i, 1, n + 1) dp[i] = vector<int>(sub[i].size()); finc(i, 0, dp[n].size()) dp[n][i] = sub[n][i].size(); fdec(i, 0, dp[n].size() - 1) dp[n][i] = max(dp[n][i], dp[n][i + 1]); fdec(i, 1, n) { finc(j, 0, sub[i].size()) { auto it = upper_bound(sub[i + 1].begin(), sub[i + 1].end(), sub[i][j]) - sub[i + 1].begin(); if (it >= sub[i + 1].size()) { dp[i][j] = -1e6; continue; } dp[i][j] = sub[i][j].size() + dp[i + 1][it]; } fdec(j, 0, sub[i].size() - 1) dp[i][j] = max(dp[i][j], dp[i][j + 1]); } if (dp[1][0] <= 0) cout << -1 << endl; else cout << dp[1][0] << endl; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int test = 1; //cin >> test; while (test--) ac(); return 0; }
K. Boomerangs
队友给我讲懂的。
可以证明,对于一个连通块,偶数个边一定可以全部选定,奇数个边一定只有一个不被选定。
如果每个点都有偶数个边,那很显然。如果有两个奇数个边的点,如果他们相连,那么每个点的边两两相消之后就会剩下这条边,我们可以把这条边消掉,让两个点的某一个剩下一条边和别人相连,当相连的那个点的度为偶数时,这条传递过来的边就被消掉了(此时边总数为偶数),否则就继续传递,只要总边是偶数,一定有方式传递到位使得全都被使用,否则还剩一条边。
基于这个理论,我们跑dfs,在最终节点开始两两相消,如果有剩余的和其父亲节点相消,相当于在dfs树上向上传递,由于dfs序里面包含所有的点,最后这条边一定能够传递到位。

#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <set> #include <vector> using namespace std; const int N = 1e5 + 10; struct node { int x, y, z; }; int v[N], fa[N]; set<int> mp[N],mp1[N]; vector<int>dele; vector<node> ans; void dfs(int x) { v[x] = 1; for (auto& I : mp[x]) { if (v[I]) continue; fa[I] = x; dfs(I); } int now = 0; dele.clear(); for (auto& I : mp1[x]) { if (I == fa[x]) continue; if (!now) now = I; else { ans.push_back({ now,x,I }); dele.push_back(now); dele.push_back(I); now = 0; } } if (now&&fa[x]) { ans.push_back({ now,x,fa[x]}); dele.push_back(now); dele.push_back(fa[x]); } for (int i = 0; i < dele.size(); i++) { int y = dele[i]; mp1[x].erase(y); mp1[y].erase(x); } } int main() { int n, m, i; scanf("%d%d", &n, &m); for (i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); mp[x].insert(y); mp[y].insert(x); mp1[x].insert(y); mp1[y].insert(x); } for (i = 1; i <= n; i++) if (!v[i]) dfs(i); printf("%d\n", ans.size()); for (i = 0; i < ans.size(); i++) printf("%d %d %d\n", ans[i].x, ans[i].y, ans[i].z); return 0; }
L. Binary String
贪心,优先删在最前面的,删到长度相等或者只剩下一个,然后从前往后删后面的,如果相等就一一对比,原数字大就答案+1,否则就输出答案。

#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <array> #include <vector> #include <map> #include <unordered_map> #include <set> #include <unordered_set> #include <stack> #include <queue> #include <bitset> #include <algorithm> #include <numeric> #include <functional> #include <cmath> using namespace std; typedef long long ll; #define finc(i,a,b) for(int i=(int)(a);i<(int)(b);i++) #define fdec(i,a,b) for(int i=(int)(b);i-->(int)(a);) #define reset(a,...) a=decltype(a)(__VA_ARGS__) #define endl '\n' #define endb ' ' #define read(a,str) scanf("%"#str,&a) #define print(a,str) printf("%"#str,a) void ac() { ll n; cin >> n; string str; cin >> str; bitset<64> inp(str); bool flag = 1; int ans = 0; fdec(i, 0, 64) { if (inp.to_ullong() <= n) { cout << ans << endl; return; } if (flag) { if (i && inp[i]) if (inp[i - 1] == 1) ans++, inp[i] = 0; else flag = 0; } else { if (inp[i]) { finc(j, i + 1, 64) inp[j - 1] = inp[j]; ans++; } } } while (inp.to_ullong() > n) inp >>= 1, ans++; cout << ans << endl; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int test = 1; //cin >> test; while (test--) ac(); return 0; }