[NOI2009]诗人小G
题面:Luogu
题解:决策单调性
Luogu上那个spj到底是哪个写的,卡行末空格和换行太屑了
设\(dp[i]\)表示前\(i\)个句子的最小不和谐度
转移很显然
\[dp[i]=\min_{0\le j<i}{\{dp[j]+val(j,i)\}} \\
val(j,i)=|sum[i]-sum[j]+i-j-1-L|^p \\
sum[i]=\sum_{j=1}^{i}len[i]
\]
由于带了\(P\)这种高次项,所以斜率优化肯定不行
于是尝试证明决策单调性
下面是《算法竞赛进阶指南》里的证明过程
要证\(\forall j<i,val(j,i+1)+val(j+1,i)\ge val(j,i)+val(j+1,i+1)\)
即证\(val(j+1,i)-val(j+1,i+1)\ge val(j,i)-val(j,i+1)\)
记\(u=(sum[i]+i)-(sum[j]+j)-(L+1)\)
记\(v=(sum[i]+i)-(sum[j+1]+j+1)-(L+1)\)
即证\(|v|^p-|v+(a[i+1]+1)|^p\ge |u|^p-|u+(a[i+1]+1)|^p\)
显然\(u>v\)
所以只需证明对于任意常数\(c\),\(f(x)=|x|^p-|x+c|^p\)单调递减
于是用导数暴力讨论即可证明
于是\(val(j,i)\)满足四边形不等式
于是\(dp\)满足决策单调性
然后就直接上而二分栈(队列)
维护三元组\(j,l,r\)表示\([l,r]\)区间最优决策是\(j\)
然后二分更新即可
注意这道题会爆\(long~long\),要开\(long~double\)
吐槽一下,第一次写二分栈感觉细节好多啊
Luogu开__int128应该可以,但是我被空格卡死了(WA30)
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T& x)
{
x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
template<typename T>
void out(T x)
{
if (x >= 10) out(x / 10);
putchar(x % 10 + '0');
}
#ifndef ONLINE_JUDGE
#define ll long long
#else
#define ll long double
#endif
const ll lim = 1e18;
int n, l, p;
inline ll qpow(ll x, int y)
{
ll ans = 1;
for (; y; y >>= 1, x = x * x)
if (y & 1) ans = ans * x;
return ans;
}
#define maxn 100005
ll dp[maxn];
char s[maxn][35];
int len[maxn], las[maxn], nex[maxn], L, R;
struct decision
{
int j, l, r;
decision() { j = l = r = 0; }
decision(int _j, int _l, int _r) :j(_j), l(_l), r(_r) {}
}q[maxn];
inline ll val(int j, int i)//这里的len[i]是原来的len[i]+i
{
return dp[j] + qpow(abs(len[i] - len[j] - 1 - l), p);
}
inline void change(int i)
{
int j = q[R].j, l = q[R].l, r = n, mid;
while (l < r)
{
mid = (l + r) >> 1;
if (val(i, mid) < val(j, mid)) r = mid;
else l = mid + 1;
}
q[R].r = l - 1;
q[++R] = decision(i, l, n);
}
int main()
{
//freopen("test.in", "r", stdin),freopen("test.out", "w", stdout);
int T; read(T);
while (T--)
{
read(n), read(l), read(p);
L = R = 1;
q[L] = decision(0, 1, n);
for (int i = 1; i <= n; ++i) scanf("%s", s[i]), len[i] = strlen(s[i]) + len[i - 1] + 1;
for (int i = 1; i <= n; ++i)
{
while (L < R && q[L].r < i) ++L;
dp[i] = val(q[L].j, i); las[i] = q[L].j;
if (val(q[R].j, n) < val(i, n)) continue;
while (val(i, q[R].l) < val(q[R].j, q[R].l)) --R;
change(i);
}
if (dp[n] > lim) puts("Too hard to arrange");
else
{
//out(dp[n]); putchar(' ');
printf("%lld", (long long)(dp[n] + 0.5));
for (int i = n; i; i = las[i]) nex[las[i]] = i;
for (int i = 1, now = 0; i <= n; i = now + 1, now = nex[now])
{
for (int j = i; j < now; ++j) printf("%s ", s[j]);
printf("%s\n", s[now]);
}
}
puts("--------------------");
}
return 0;
}
一切伟大的行动和思想,都有一个微不足道的开始。
There is a negligible beginning in all great action and thought.
There is a negligible beginning in all great action and thought.