\(2000\) 分的题我赛场上居然没有做出 qwq
想得太复杂了。
\(n\) 列数每列取一个,\(m\) 种取法不行,求和最大的取法。
受到以前贪心讲课的一道题的影响,看到我就想着把前 \((m+1)\) 大的方案都求出来,存进 \((m+1)\) 个 twt
里面,然后排个序看看哪个没出现就是了。
考虑两个的合并,若 \((i, j)\) 是当前最大的,那么下一个大的可能是 \((i+1, j)\) 或 \((i, j+1)\),这两个塞进优先队列里就好了,把这个过程重复 \((m-1)\) 次就可以得到前 \(m\) 大的。
代码#include <iostream>
#include <set>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>
#include <map>
#define int long long
const int base = 2333, P = 10'0000'0103;
int n, k, m;
std::vector<int> a[15];
struct dwd {
int a, x, y;
dwd(int _a, int _x, int _y) { a = _a, x = _x, y = _y; }
bool operator < (dwd b) const {
if (a != b.a) return a > b.a ;
if (x != b.x) return x < b.x;
if (y != b.y) return y < b.y;
return false;
}
};
struct twt {
std::vector<int> b;
int val, hash;
void push(int x) {
int now = b.size();
b.push_back(x), val += a[now+1][x];
hash = (hash * base % P + a[now+1][x]) % P;
}
twt(int x) { hash = 0, val = 0; b.clear(); push(x); }
twt() { hash = 0, b.clear(); val = 0; }
bool operator < (const twt &z) const {
if (val != z.val) return val > z.val;
else return hash < z.hash;
}
bool operator != (const twt &z) {
for (int i = 0; i < n; i++) if (b[i] != z.b[i]) return true;
return false;
}
friend std::ostream& operator << (std::ostream& out, twt x) {
for (int i = 0; i < (int)x.b.size(); i++)
out << a[i+1][0]-x.b[i]+1 << ' ';
return out;
}
};
std::vector<twt> s, tmp, fo;
std::set<dwd> t;
std::map<twt, bool> map;
void merge(int q) {
t.clear(), tmp.clear();
t.insert(dwd(s[0].val + a[q][1], 0, 1));
while (tmp.size() <= m+1 && !t.empty()) {
int i = t.begin()->x, j = t.begin()->y;
t.erase(t.begin());
twt cur = s[i];
cur.push(j);
tmp.push_back(cur);
if (i+1 < (int)s.size()) t.insert(dwd(s[i+1].val + a[q][j], i+1, j));
if (j+1 <= a[q][0]) t.insert(dwd(s[i].val + a[q][j+1], i, j+1));
// cnt ++;
}
s = tmp;
std::sort(s.begin(), s.end());
}
signed main() {
std::cin >> n;
for (int i = 1; i <= n; i++) {
std::cin >> k;
a[i].resize(k+1), a[i][0] = k;
for (int j = 1; j <= k; j++) std::cin >> a[i][j];
std::sort(std::next(a[i].begin()), a[i].end(), std::greater<int>());
}
std::cin >> m;
for (int i = 1; i <= a[1][0]; i++) s.push_back(twt(i));
std::sort(s.begin(), s.end());
for (int i = 2; i <= n; i++) merge(i);
for (int i = 1; i <= m; i++) {
twt cur;
for (int j = 1; j <= n; j++) std::cin >> k, cur.push(a[j][0]-k+1);
map[cur] = 1;
}
int j = 0;
for (auto cur : s) if (!map[cur]) return std::cout << cur, 0;
}
赛时因为 比较没有严格弱序 + 奇怪的错误 没能通过。
其实还是因为思维不够,马量来凑。
稍微观察就可以发现答案要么是每组取最大,要么是某个被禁掉的某一位少一个,那么枚举就好了。
就 是 这 么 简 单
#include <iostream>
#include <vector>
#include <map>
#define int long long
int n, m, mx;
std::vector<int> a[15], ab[100005], v, ans;
std::map<std::vector<int>, bool> map;
std::ostream& operator << (std::ostream& out, std::vector<int> a) {
for (int i = 0; i < (int)a.size(); i++) out << a[i] << ' ';
return out;
}
signed main() {
std::ios::sync_with_stdio(false), std::cin.tie(nullptr);
std::cin >> n;
for (int i = 1, k; i <= n; i++) {
std::cin >> k;
a[i].resize(k+1), a[i][0] = k, v.push_back(a[i][0]);
for (int j = 1; j <= a[i][0]; j++) std::cin >> a[i][j];
}
ans = v;
std::cin >> m;
for (int i = 1, k; i <= m; i++) {
v.clear();
for (int j = 1; j <= n; j++) std::cin >> k, v.push_back(k);
map[v] = 1, ab[i] = v;
}
if (!map.count(ans)) return std::cout << ans, 0;
for (int i = 1; i <= m; i++) {
for (int j = 0; j < n; j++) {
if (ab[i][j] <= 1) continue;
ab[i][j] --;
int an = 0;
for (int k = 1; k <= n; k++) an += a[k][ab[i][k-1]];
if (!map.count(ab[i]) && an > mx) mx = an, ans = ab[i];
ab[i][j] ++;
}
}
std::cout << ans;
}
所以比赛时别着急写,这种“老题翻新”系列自有其自己的做法,怎么可能让你把原来的做法改一改就过呢?