====== Codeforces Round #655 EF题解 ====== ===== E. Omkar and Last Floor ===== * 题意:一个n*m矩阵,每一行被分割成了多个片段,每个片段中填一个1,使每列1个数的平方和最大。输出最大结果。m,n<100 * 分类:dp、贪心 由于结果为每列1个数的平方,因此1需要尽可能多地集中到1列。 dp[l][r] 表示水平区间[l,r]内的最优解,如果一个片段一部分在[l,r]外则不计入结果。考虑1尽可能集中到第i列,那么有dp[l][r] = dp[l, i-1] + dp[i+1, r] + 被i分割,且位于[l,r]区间内的的片段数 对枚举i后取最大值即可得到dp[l][r] * comment: 这道题m,n较小,可以容纳维度更高的dp。主要是需要确定dp时分割片段的标准。 #include using namespace std; int tem = 0; int ll[200][200], rr[200][200]; int dp[200][200] = {}; int n; int dfs(int l, int r) { if(l > r) return 0; if (dp[l][r] >= 0)return dp[l][r]; int m = 0; for (int i = l; i <= r; ++i) { int tot = 0; for (int j = 0; j < n; ++j) { if (ll[j][i] >= l && rr[j][i] <= r) tot++; } m = max(m, tot * tot + dfs(l, i - 1) + dfs(i + 1, r)); } return dp[l][r] = m; } int main() { int m; cin >> n >> m; for (int i = 0; i < n; ++i) { int k; scanf("%d", &k); for (int j = 0; j < k; ++j) { int l, r; scanf("%d%d", &l, &r); for (int i1 = l; i1 <= r; ++i1) { ll[i][i1] = l; rr[i][i1] = r; } } } for (int i = 0; i <= m; ++i) { for (int j = 0; j <= m; ++j) { dp[i][j] = -1; } } cout << dfs(1, m); } ===== F. Omkar and Modes ===== * 题意:存在一个不减的数列,每次询问可以返回区间内出现次数最多的数字和其个数,在4k-4个询问内确定这个数列 * 分类:CDQ分治 显然需要用类似二分的方式询问,不过二分的次数为log k * k,log k可能会大于4 但是还有额外的信息可以利用:数列是递增的。相同的数字必然相邻。 对于一个询问的两个子区间,如果左子区间和父区间结果数字相同,且数字个数不同,则可以推断出该数字在父区间内的全部覆盖范围,搜索剩余片段即可。如果结果数字不同,则父区间询问结果与右区间完全相同,不再需要再次询问右区间。 另外,为了尽可能阻止出现左子区间和父区间结果完全相同,需要再次二分的情况,二分的中值设定为了父区间的数字最大重复个数。 因此大约每两次询问可以定位一个数字。 * comment:这道题的询问次数限制很少,分治时要特别注意log的消除 #include using namespace std; int a[200005]; map mp; int xx = 0, ff = 0; pair chk(int l, int r, int prex, int pref) { if (r < l) return {-1, -1}; if (!ff)printf("? %d %d\n", l, r); fflush(stdout); int x = xx, f = ff; if (!ff) { scanf("%d", &x); assert(x != -1); scanf("%d", &f); } xx = 0; ff = 0; if (f >= r - l + 1) { for (int i = l; i <= r; ++i) { a[i] = x; } if (!mp.count(x))mp[x] = 0; mp[x] += r - l + 1; } else if (x == prex) { for (int i = 0; i < f; ++i) { a[r] = x; r--; mp[x]++; } chk(l, r, 0, 0); } else { int oldx = mp.count(x) ? mp[x] : 0; chk(l, l + f - 1, x, f); int j = mp[x] - oldx; int i = l + f; if (!j) { xx = x; ff = f; chk(i, r, prex, pref); } else { int cnt = f - j; if (cnt < f) { if (!mp.count(x))mp[x] = 0; while (cnt-- > 0 && i <= r) { a[i] = x; mp[x]++; i++; } } chk(i, r, prex, pref); return {x, f}; } } return {x, f}; } int main() { int n; cin >> n; chk(1, n, 0, 0); printf("! "); for (int i = 1; i <= n; ++i) { printf("%d ", a[i]); } }