原文链接 https://www.cnblogs.com/cly-none/p/ECFINAL2018J.html

题意:给出一个长度为\(n\)的字符串\(s\),要求给\(s\)的每个后缀\(s[i:]\)分配权值\(k_i\)(实数),满足\(0 \leq k_i \leq 1\),且\(\sum_i k_i = 1\)。再此基础上,最大化

\[\min_{i=1}^n \left( \sum_{j=1}^n k_j {\rm {lcp}} (s[i:],s[j:]) \right)
\]

\(n \leq 2 \times 10^5\)

第一步当然是构建SAM(反串),令\(s[i:]\)在parent树上对应的结点为\(p_i\),\(dep_i\)为结点\(i\)所能表示的最长串,那么我们要最大化的就是\(\min_{i=1}^n \left( \sum_{j=1}^n k_j dep_{{\rm {lca}} (p_i,p_i)} \right)\)。

在这里树的结构恰好为权值的计算提供了一种类似分治的结构,因此我们考虑用树上dp解决这个问题。

于是我们令\(dp_i\)为仅考虑结点\(i\)的子树的答案。如果\(i\)是后缀结点,那么显然\(dp_i = dep_i\)。这样就确定了初始值。

然后就考虑合并。我们设结点\(u\)有孩子结点\(v\)。那么假设我们把\(k\)点权值分配到\(v\)的子树中,\(v\)能对答案产生的贡献就是\(dp_v k + dep_u (1-k) = (dp_v - dep_u)k + dep_u\)。我们所要做的就是对这些一次函数分配\(k\),使\(k\)的总和为\(1\),且它们的值取\(\min\)后尽可能大。注意到这些一次函数的斜率都大于零,这意味着如果分配完\(k\)后这些函数的取值不互相相等,那么可以把当前函数值最小的那个调大,当前函数值最大的那个调小,得到一个更优的答案。

因此我们要做的就是确定一条如下图所示的平行于\(x\)轴的直线,使得所有函数对应的\(x\)值之和为\(1\)。这样得到的解显然是合法的。

这当然可以二分。也能得到是根据以斜率的倒数为比例分配的。

这样就能\(O(n)\)解决本题了。

此外,这道题还有在笛卡尔树上dp的做法,与本做法没有太大区别。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
#define fir first
#define sec second
#define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i)
#define rrp(i,a,b) for (int i = (a) ; i >= (b) ; -- i)
#define gc() getchar()
template <typename tp>
inline void read(tp& x) {
x = 0; char tmp; bool key = 0;
for (tmp = gc() ; !isdigit(tmp) ; tmp = gc())
key = (tmp == '-');
for ( ; isdigit(tmp) ; tmp = gc())
x = (x << 3) + (x << 1) + (tmp ^ '0');
if (key) x = -x;
} const int N = 200010;
int ch[N << 1][26], fa[N << 1], len[N << 1], cnt = 1, las = 1;
bool tag[N << 1];
void append(char c) {
int np = ++ cnt, p = las;
tag[np] = 1;
las = np;
len[np] = len[p] + 1;
fa[np] = 1;
while (p && !ch[p][c - 'a'])
ch[p][c - 'a'] = np, p = fa[p];
if (!p) return;
int q = ch[p][c - 'a'];
if (len[q] == len[p] + 1)
fa[np] = q;
else {
int nq = ++ cnt;
len[nq] = len[p] + 1;
tag[nq] = 0;
fa[nq] = fa[q];
rep (i, 0, 25) ch[nq][i] = ch[q][i];
fa[q] = nq;
fa[np] = nq;
while (p && ch[p][c - 'a'] == q)
ch[p][c - 'a'] = nq, p = fa[p];
}
}
struct edge {
int la,b;
} con[N << 1];
int tot,fir[N << 1];
void add(int from,int to) {
con[++tot] = (edge) {fir[from], to};
fir[from] = tot;
}
char str[N];
int n;
db dp[N << 1];
void dfs(int pos) {
if (tag[pos]) {
dp[pos] = len[pos];
return;
}
db tmp = 0;
for (int i = fir[pos] ; i ; i = con[i].la)
dfs(con[i].b), tmp += 1.0 / (dp[con[i].b] - len[pos]);
dp[pos] = 1.0 / tmp + len[pos];
}
void solve() {
scanf("%s", str + 1);
n = strlen(str + 1);
tot = 0;
las = cnt = 1;
memset(fir,0,sizeof(int) * (2 * (n + 5)));
memset(ch,0,sizeof(int) * (26 * 2 * (n + 5)));
rrp (i, n, 1) append(str[i]);
rep (i, 2, cnt) add(fa[i], i);
dfs(1);
printf("%.10lf\n", dp[1]);
}
int main() {
int T;
read(T);
while (T --)
solve();
return 0;
}

小结:这种问题因为能把贡献规约到每个lca处,所以可以用树形dp解决。这和笛卡尔树的分治思路是同等的。

【做题】ECFinal2018 J - Philosophical … Balance——dp的更多相关文章

  1. 2018ECfinal J. Philosophical Balance

    2018ECfinal J. Philosophical Balance 题目大意: 给出一个字符串 \(s\) ,你需要给每一个 \(i\) 一个 \([0,1]\) 之间的权值 \(k_i\) , ...

  2. 2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)

    题目链接 题意 :小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100,告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的 ...

  3. 【做题记录】DP 杂题

    P2577 [ZJOI2004]午餐 $\texttt{solution}$ 想到贪心: 吃饭慢的先打饭节约时间, 所以先将人按吃饭时间从大到小排序. 状态: \(f[i][j]\) 表示前 \(i\ ...

  4. DP 优化方法大杂烩 & 做题记录 I.

    标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...

  5. 【做题】CF388D. Fox and Perfect Sets——线性基&数位dp

    原文链接https://www.cnblogs.com/cly-none/p/9711279.html 题意:求有多少个非空集合\(S \subset N\)满足,\(\forall a,b \in ...

  6. 【做题】TCSRM601 Div1 500 WinterAndSnowmen——按位考虑&dp

    原文链接https://www.cnblogs.com/cly-none/p/9695526.html 题意:求有多少对集合\(S,T\)满足:\(S \subseteq \{1,2...n \}, ...

  7. 【做题】spoj4060 A game with probability——dp

    赛前做题时忽然发现自己概率博弈类dp很弱,心好慌.(获胜概率或最优解期望) 于是就做了这道题,续了特别久. 一开始列dp式子的时候就花了很长时间,首先搞错了两次,然后忘记了根据上一轮dp值直接确定选什 ...

  8. bzoj5108 [CodePlus2017]可做题 位运算dp+离散

    [CodePlus2017]可做题 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 87  Solved: 63[Submit][Status][Dis ...

  9. 【做题】TCSRM592 Div1 500 LittleElephantAndPermutationDiv1——计数&dp

    题意:定义函数\(f(A,B) = \sum_{i=1}^n \max(A_i,B_i)\),其中\(A\)和\(B\)都是长度为\(n\)的排列.给出\(n\)和\(k\),问有多少对\((A,B) ...

随机推荐

  1. 反射的应用,jdbc封装

    实现在Java中查询数据库并保存在Java中 1.创建Dept类(要查找的类) package cn.ljs; public class Dept { private int deptno; priv ...

  2. CF1033G Chip Game

    题意 给你一个长度为\(n\)的序列和一个数\(m\). 小A和小B分别在\([1,m]\)选出一个数\(a\)和\(b\),然后开始游戏. 轮到小A时,他选择一个元素减\(a\):小B则选择一个元素 ...

  3. html转markdown网站

    戳下面的链接,可以直接复制富文本粘贴编程markdown: https://euangoddard.github.io/clipboard2markdown/

  4. nginx修改上传文件大小限制

    问题: 项目上线,图片上传报413错误,找了半天,原来是nginx限制了上传大小 在nginx.conf的server的location中加client_max_body_size 10m;

  5. Spark入门到精通--(第十节)环境搭建(ZooKeeper和kafka搭建)

    上一节搭建完了Hive,这一节我们来搭建ZooKeeper,主要是后面的kafka需要运行在上面. ZooKeeper下载和安装 下载ZooKeeper 3.4.5软件包,可以在百度网盘进行下载.链接 ...

  6. Alibaba, I'm interested in you.

    Working for Alibaba is an aspiration for some. For other it’s the possibility of lucrative stock opt ...

  7. Windows下配置node和npm

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效. Node.js 的使用包管理器 ...

  8. mysql中的concat,concat_ws(),group_concat()

    mysql中的concat,concat_ws(),group_concat() 说明: 本文中使用的例子均在下面的数据库表tt2下执行:     一.concat()函数 1.功能:将多个字符串连接 ...

  9. vs2015官方下载链接

    https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downl ...

  10. h5页面适配小结

    大概是去年的7月想写这个内容去加深自己的理解.现在终于回来补上这篇入门小结了. 1.问题描述 适配的目标:在不同尺寸的手机设备上,页面“相对性的达到合理的展示(自适应)”或者“保持统一效果的等比缩放( ...