CodeForces 1070J Streets and Avenues in Berhattan 性质+动态规划
题目大意:
你有$k$个数,分为$26$种
对于每个数,你可以选择选进$A$集合或者$B$集合或者不选
要求$A$集合中必须有$n$个数,$B$集合中必须有$m$个数
记第$i$种数在$A$集合中的个数为$a_i$,$B$中个数为$b_i$
试最小代价$\sum a_i * b_i$
$k \leqslant 200000$,$n, m \leqslant 30000$
首先,我们打表得出一个结论,代价一定只由一种数得出
考虑证明:
我们不妨设代价由$A$得出,且集合$S_A$和$S_B$中分别有$i$个$A$和$a - i$个$A$
那么,如果我们尝试用$B$来替换$A$,不妨设从$B$中抽了$j$个$B$扔进$A$中,且一共有$b$个$B$
那么贡献差为$i * (a - i) - ((i - j) * (a - i + j) + j * (b - j))(0 \leq j \leq min(i, b))$
化简后,为$2j^2 - j(2i - a+ b)$
这是一个开口向上的,以$j$为自变量的二次函数
最大值一定在端点取到,也就是$j = 0$或者$j = i$或者$j = b$取到
这三种情况对应着代价由$A$得出或者由$B$得出
我们可以枚举在中间的种类是哪一个
之后再枚举放在$A$中的数能取多少个
相应地我们可以知道最多可以放在$B$中多少个
可以通过背包来实现上述问题
由于实现不优,复杂度为$O(26^2 * (n+ m) + 26 * k)$
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define ri register int #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } int wr[50], rw; #define pc(iw) putchar(iw) tpr inline void write(ra o, char c = '\n') { if(!o) pc('0'); if(o < 0) o = -o, pc('-'); while(o) wr[++ rw] = o % 10, o /= 10; while(rw) pc(wr[rw --] + '0'); pc(c); } } using namespace std; using namespace remoon; #define sid 30050 #define kid 200050 char s[kid]; bool f[kid]; int n, m, k, t, cnt[50]; inline int judge() { f[0] = 1; memset(f, 0, sizeof(f)); rep(i, 0, 25) drep(j, k, cnt[i]) f[j] |= f[j - cnt[i]]; rep(i, n, k - m) if(f[i]) { puts("0"); return 1; } return 0; } inline void solve() { int ans = 1e9; rep(i, 0, 25) { memset(f, 0, (n + m) << 2); f[0] = 1; rep(j, 0, 25) if(i != j) drep(v, n + m, cnt[j]) f[v] |= f[v - cnt[j]]; rep(j, 0, n + m) if(f[j]) { int l = max(n - j, 0); int r = max(m - (k - cnt[i] - j), 0); if(l + r <= cnt[i]) ans = min(ans, l * r); } } write(ans); } int main() { t = read(); while(t --) { n = read(); m = read(); k = read(); scanf("%s", s + 1); int sn = strlen(s + 1); memset(cnt, 0, sizeof(cnt)); rep(i, 1, sn) ++ cnt[s[i] - 'A']; if(judge()) continue; solve(); } return 0; }
喵喵喵?喵喵喵! 喵喵喵......