当前位置: 首页 > news >正文

P1912 [NOI2009] 诗人小G 分析

题目链接:P1912 [NOI2009] 诗人小G

题目概述

给你几个字符串,你可以按照给定的顺序任意拼接(你可以分组),但是拼接的时候中间要打空格,设这个当前的拼接长度为 \(sum\),那么代价为 \(|sum-L|^P\),求最小的代价并输出方案。

分析

Luogu题解告诉我们一个快速判断决策单调性的办法:https://www.luogu.com.cn/article/38xahpje。

首先能够决策单调性优化的 \(dp\) 是:\(f_i=\min f_j+w(j,i)\),最优决策点为 \(p_i\),若都满足 \(p_i\leq p_{i+1}\) 那么就满足决策单调性。

设决策点为 \(j\) 的函数 \(f_j(x)=f_j+w(j,x)\),假设两个决策点 \(j<l\),如果 \(f_j(x)\)\(f_l(x)\) 有且仅有一个交点,那么其满足决策单调性。

步入正题。

首先显然:

\[f_i=\min_{j\in[0,i-1]}f_j+|sum_i-sum_j-1-L|^P \]

我们感性理解一下就知道这满足决策单调性(你也可以打表证明)。

虽然说这道题目是一道经典题,我们还是要记录许多 trick 的。

传统的决策单调性二分队列写法十分的骚气。

按照以下过程执行:

队列中每一个值存三个状态 \((k,l,r)\),代表决策点 \(k\)\([l,r]\) 有效。

对于队头中 \(r\geq i\) 的弹出。

然后用队头进行转移。

对于当前决策点 \(i\),如果后面队尾的 \(l\) 决策都比我菜,那你也没有存在的必要了。如果只是 \(r\) 比我菜,那么我们二分这个然后分别赋值就可以了。

我们发现有些 \(l,r\) 是不是没有必要,我们只需要维护相邻两个决策点的临界值即可。

对于此题,我们有可能爆 long long,可以通过精度(long double)来换取。

代码

时间复杂度 \(\mathcal{O}(n\log n\log P)\)

#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define int long long
#define ld long double
#define N 100005
using namespace std;
#define abs(x) ((x) < 0 ? -(x) : (x))
#define isdigit(ch) ('0' <= ch && ch <= '9')
template<typename T>
void read(T&x) {x = 0;char ch = getchar();for (;!isdigit(ch);ch = getchar());for (;isdigit(ch);ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
}
template<typename T>
void write(T x) {if (x > 9) write(x / 10);putchar(x % 10 + '0');
}
ld qpow(ld a,int b) {ld res = 1;while(b) {if (b & 1) res *= a;a *= a;b >>= 1;}return res;
}
int T,len[N],n,P,L,sum[N],q[N],p[N],pre[N];
ld f[N];
string s[N];
ld calc(int i,int j) {return f[j] + qpow(abs(sum[i] - sum[j] - L),P);
}
int find(int x,int y) {int l = x,r = n + 1,res = n + 1;while (l <= r) {int mid = l + r >> 1;if (calc(mid,x) >= calc(mid,y)) res = mid,r = mid - 1;else l = mid + 1;}return res;
}
signed main(){read(T);for (;T --;) {read(n),read(L),read(P);L ++;for (int i = 1;i <= n;i ++) {cin >> s[i];len[i] = s[i].size();}for (int i = 1;i <= n;i ++) sum[i] = sum[i - 1] + len[i] + 1,f[i] = 1e18 + 5;f[0] = 0;int head = 1,tail = 0;q[++tail] = 0;for (int i = 1;i <= n;i ++) {while(head < tail && p[head] <= i) head ++;f[i] = calc(i,q[head]);pre[i] = q[head];while(head < tail && p[tail - 1] >= find(q[tail],i)) tail --;p[tail] = find(q[tail],i);q[++tail] = i;}if (f[n] > 1e18) puts("Too hard to arrange");else {write((int)(f[n] + 0.5)),putchar('\n');int now = n;vector<string> ls;while(now) {ls.push_back("");for (int i = now;i > pre[now];i --)ls.push_back(s[i]);now = pre[now];}reverse(ls.begin(),ls.end());bool flag = 0;for (auto i : ls) {if (i == "") putchar('\n'),flag = 0;else {if (flag) putchar(' ');cout << i;flag = 1;}}}puts("--------------------");}return 0;
}
http://www.hskmm.com/?act=detail&tid=31887

相关文章:

  • [COCI2022-2023#2] Tramvaji 题解
  • 一级指针和二级指针作为函数参数的区别
  • ROUGE指标
  • CSP-S 模拟 29
  • Linux 文件及相关安全操作指南
  • day012
  • 怎么能把一个横着的很长的excel表,输出成一个能完整展示在一个页面中的PDF
  • 高精度
  • 深入解析:Leetcode+Java+图论+岛屿问题
  • 简单介绍
  • agent技术框架
  • agent认知与原理分析
  • agent策略分析与Parer解读
  • Visual Studio 2022连接mysql数据库,解决System.Data.Odbc.OdbcException (0x80131937)
  • day05
  • [AI生成]Spark-TTS个人理解
  • 2025.10.3 测试
  • [20251015]建立和完善col_vlist.sql脚本.txt
  • [20251014]建立和完善col_list.sql脚本.txt
  • [20251014]建立完善通用的prx.sql脚本.txt
  • 倍增法
  • 复杂版式与印章干扰下的高精度社会团体法人登记证书识别技术
  • 征程 6 | BPU trace 简介与实操
  • 2025年预应力千斤顶厂家最新权威推荐榜:批发采购、张拉设备、同步顶升系统专业供应商综合测评与选购指南
  • 2025.10.15训练记录
  • 利用Next.js中间件漏洞实现SSRF攻击与RCE
  • 三级医疗服务体系 (Three Tiers of Care)
  • 2025年瑕疵检测设备厂家最新推荐排行榜,表面瑕疵检测,薄膜瑕疵检测,铝箔瑕疵在线检测,外观瑕疵检测机公司推荐!
  • 2025年冷却塔厂家最新推荐排行榜:高效制冷与稳定性能之选!
  • 牛客2025秋季算法编程训练联赛1