【NOI2009】诗人小G
题解
决策单调性优化的一道题。
对于一个转移 f[i,j]=min{f[i][k]+f[k+1][j]+w(i,j)}, 如果w(i,j)满足四边形不等式和区间包含单调,那么f[i,j]也满足四边形不等式和区间包含单调。而四边形不等式就意味着最优决策点是单调的。这个证明可在很多论文中找到。
而这题的特殊之处在于,对于转移 f[i]=min, 只需要w满足四边形不等式,f就满足决策单调。
给出证明:
设 x 的最优决策点为 i, x + 1 的最优决策点为 j。如果结论不成立,那么有j<i<x<x+1
根据四边形不等式,我们已经知道w(j,x+1)+w(i,x) >= w(i,x+1) + w(j,x)
因为 j 不是 x 的最优决策点, i 不是 x + 1 的最优决策点,因此有:
$ f[j] + w(j,x) >= f[x] f[i] + w(i,x + 1) >= f[x + 1] $
两式相加,我们就得到 :
w(j,x) + w(i, x + 1) >= (f[x] - f[i]) + (f[x + 1] - f[j]) = w(i, x) + w(j, x + 1)
这就和四边形不等式矛盾了。
这道题里面,f[i] = \min\{f[j] + w(j, i)\}, 其中w(j,i)等于[j+1,i]长度产生的代价。
分讨求导一下就可以得到w(j,i)满足四边形不等式,由上得出f满足决策单调。
维护一个决策点的单调队列,记下每个决策点比前一个决策点优的时间。这个时间可以在加入队列的时候二分得到。需要注意的是如果加入一个新元素时,它比队尾优的时间早于队尾比前一个决策点优的时间,那么队尾就应该被弹出。
这题爆long long非常麻烦,long double可以水过去。
#include<bits/stdc++.h>
#define fi first
#define se second
#define LL long long
#define LD long double
#define pii pair<int, int>
using namespace std;
const int N = 1e5 + 10;
const LL LIMIT = 1e18;
template<typename T> void read(T &x) {
char c = getchar(); int f = 0;
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
for (x = 0; c >= '0' && c <= '9'; c = getchar())
x = (x << 3) + (x << 1) + (c ^ '0');
if (f) x = -x;
}
LL n, L, p;
LL a[N], pre[N];
LD f[N], sum[N];
char s[N][35];
int h, t;
pii q[N];
LD Qpow(LD x, int p) {
LD ans = 1;
while (p) {
if (p & 1) ans = ans * x;
x = x * x;
p >>= 1;
}
return ans;
}
LD w(int x, int y) {
return Qpow(abs(sum[y] - sum[x] + y - x - 1 - L), p);
}
void solve() {
read(n); read(L); read(p);
for (int i = 1; i <= n; ++i) {
scanf("%s", s[i] + 1);
a[i] = strlen(s[i] + 1);
sum[i] = sum[i - 1] + a[i];
}
f[0] = 0;
h = 1, t = 0;
q[++t] = make_pair(0, 1);
for (int i = 1; i <= n; ++i) {
while (t - h >= 1 && q[h + 1].se <= i) ++h;
int x = q[h].fi;
pre[i] = x;
f[i] = f[x] + w(x, i);
while (h <= t) {
int y = q[t].fi;
LD s1 = f[y] + w(y, max(i + 1, q[t].se));
LD s2 = f[i] + w(i, max(i + 1, q[t].se));
if (s2 <= s1) --t;
else break;
}
if (h > t) q[++t] = make_pair(i, i + 1);
else {
int x = q[t].fi;
int L = max(i, q[t].se) + 1, R = n, ans = n + 1;
while (L <= R) {
int mid = (L + R) >> 1;
LD s1 = f[x] + w(x, mid);
LD s2 = f[i] + w(i, mid);
if (s2 <= s1) {
R = mid - 1;
ans = mid;
}
else L = mid + 1;
}
if (ans <= n) q[++t] = make_pair(i, ans);
}
}
if (f[n] > LIMIT) {
puts("Too hard to arrange");
}
else {
cout << (LL)f[n] << endl;
stack<int> st;
int now = n;
while (now) {
st.push(now);
now = pre[now];
}
int las = 0;
while (!st.empty()) {
int x = st.top(); st.pop();
for (int i = las + 1; i < x; ++i) {
printf("%s ", s[i] + 1);
}
printf("%s\n", s[x] + 1);
las = x;
}
}
puts("--------------------");
}
int main() {
int T; read(T);
while (T--) solve();
return 0;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· Vite CVE-2025-30208 安全漏洞
· 《HelloGitHub》第 108 期
· MQ 如何保证数据一致性?
· 一个基于 .NET 开源免费的异地组网和内网穿透工具