123789456ye

已AFO

[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;
}
posted @ 2020-03-21 11:16  123789456ye  阅读(122)  评论(0编辑  收藏  举报