P8544 「Wdoi-2」禁断之门对面,是此世还是彼世
*K. P8544 「Wdoi-2」禁断之门对面,是此世还是彼世
很棒的一道题目。
先考虑一层
考虑
上述分析使得我们只需要求出
进一步地,同一行元素互不相同,这让我们想到二分图匹配。所以
很显然,我们可以用费用流解决上述问题,其中左部点
回到上述问题,我们发现使
- 若存在
a < b < c < d 使得a, c 在同一个环,b, d 在同一个环,则交换b, c 一定不会使答案变劣。即 环不相交。
这说明每个环占用一段连续区间
- 若存在
a < b < c < d 使得a \to c \to b \to d ,则交换b, c 一定更优,即 边不交叉。
这说明每个环
上述两条结论对环的形态做出了相当严格的规范,已经可以暴力 DP
观察长度为
接下来考虑
结合
类似地,我们将链的转移写成类插头 DP 的形式,设
void trans() {
memset(f, 0x3f, sizeof(f));
f[0][0][0] = f[1][0][0] = 0;
for(int i = 2; i <= m; i++) {
memcpy(f[i], f[i - 1], sizeof(f[i]));
for(int j = 1; j <= t; j++) {
f[i][j][1] = min(f[i - 1][j - 1][1], f[i - 2][j - 1][0]) + b[i - 1] + b[i];
}
for(int j = 2; j <= t; j++) {
ll coef = 2 * (b[i - 1] + b[i]);
f[i][j][0] = min(f[i][j][0], f[i - 2][j - 2][0] + coef);
}
if(i > 2) {
for(int j = 3; j <= t; j++) {
ll coef = 2 * (b[i - 2] + b[i]) + 3 * b[i - 1];
f[i][j][0] = min(f[i][j][0], f[i - 3][j - 3][0] + coef);
}
}
for(int j = 0; j <= t; j++) f[i][j][0] = min(f[i][j][0], f[i][j][1]);
}
}
这样可以做到小常数
看起来没有什么优化空间了。但这种限制了选取物品个数,且答案关于物品个数具有凸性的 DP,很难不让人想到 wqs 二分。想到这里,结合
实际上 “很难不让人想到 wqs” 这句话是在口嗨,因为我自己就没想到。我分析到
吐槽一句:
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define fi first
#define se second
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using ull = unsigned long long;
inline ll read() {
ll x = 0, sgn = 0;
char s = getchar();
while(!isdigit(s)) sgn |= s == '-', s = getchar();
while(isdigit(s)) x = x * 10 + s - '0', s = getchar();
return sgn ? -x : x;
}
inline void print(int x) {
if(x < 0) return putchar('-'), print(-x);
if(x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
bool Mbe;
constexpr int N = 5e5 + 5;
constexpr int mod = 1e9 + 7;
constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
void add(int &x, int y) {x += y, x >= mod && (x -= mod);}
struct dat {
ll val, num;
dat operator + (const dat &x) const {return {val + x.val, num + x.num};}
bool operator < (const dat &x) const {
return val != x.val ? val < x.val : num < x.num;
}
} f[N][2];
int n, m, t, coef;
ll b[N];
int calc(ll mid) {
f[0][0] = f[1][0] = {0, 0};
f[0][1] = f[1][1] = {inf, 0};
for(int i = 2; i <= m; i++) {
f[i][0] = f[i - 1][0];
dat coef = {b[i - 1] + b[i] - mid, 1};
f[i][1] = min(f[i - 1][1], f[i - 2][0]) + coef;
coef = {2 * (b[i - 1] + b[i]) - 2 * mid, 2};
f[i][0] = min(f[i][0], f[i - 2][0] + coef);
if(i > 2) {
coef = {2 * (b[i - 2] + b[i]) + 3 * b[i - 1] - 3 * mid, 3};
f[i][0] = min(f[i][0], f[i - 3][0] + coef);
}
f[i][0] = min(f[i][0], f[i][1]);
}
return f[m][0].num;
}
bool Med;
int main() {
fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
#ifdef ALEX_WEI
FILE* IN = freopen("1.in", "r", stdin);
FILE* OUT = freopen("1.out", "w", stdout);
#endif
cin >> n >> m >> t;
for(int i = 1; i <= n; i++) add(coef, read());
for(int i = 1; i <= m; i++) b[i] = read();
ll l = 1, r = 3e9;
while(l < r) {
ll mid = l + r + 2 >> 1;
if(calc(mid) <= t) l = mid;
else r = mid - 1;
}
calc(l);
cout << (f[m][0].val + l * t) % mod * coef % mod << endl;
cerr << TIME << " ms\n";
return 0;
}