BZOJ 3238 差异

看这个式子其实就是求任意两个后缀的 $ LCP $ 长度和。前面的 $ len(T_i)+len(T_j) $ 求和其实就是 $ n(n-1)(n+1)/2 $ ,这个是很好推的。。

任意两个后缀的 $ LCP $ 长度和很容易想到构造 height 数组,然后问题就变成了所有区间的最小值的和。

这是个套路题,可以单调栈,但是其实分治也很好写!

设我们要求的区间是 $ [l,r] $ 我们可以找出其中最小值所在的位置,这个可以ST表快速求,然后从这个位置进行分治。

这样的分治每进行一次,总有效的元素数量会减少1,因此复杂度是 $ O(nlogn) $ 的。

开始有个地方漏了 1ll 。。。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 500006
#define C 130
namespace wtf {
char ch[MAXN];
int sa[MAXN], tp[MAXN], rnk[MAXN], buc[MAXN], len;
int P[MAXN][20], ht[MAXN]; int que(int l, int r) {
int L = (31 - __builtin_clz(r - l + 1));
return (ht[P[l][L]] < ht[P[r - (1 << L) + 1][L]] ? P[l][L] : P[r - (1 << L) + 1][L]);
} void init() {
len = strlen(ch + 1);
int m = C;
for (int i = 1; i <= len; ++i) ++buc[rnk[i] = ch[i]];
for (int i = 1; i <= m; ++i) buc[i] += buc[i - 1];
for (int i = len; i >= 1; --i) sa[buc[rnk[i]]--] = i;
for (int k = 1, p = 0; p < len; k <<= 1) {
p = 0;
for (int i = 0; i <= m; ++i) buc[i] = 0;
for (int i = len - k + 1; i <= len; ++i) tp[++p] = i;
for (int i = 1; i <= len; ++i) if (sa[i] > k) tp[++p] = sa[i] - k;
for (int i = 1; i <= len; ++i) ++buc[rnk[i]];
for (int i = 1; i <= m; ++i) buc[i] += buc[i - 1];
for (int i = len; i >= 1; --i) sa[buc[rnk[tp[i]]]--] = tp[i];
p = 1;
swap(rnk, tp);
rnk[sa[1]] = 1;
for (int i = 2; i <= len; ++i)
rnk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + k] == tp[sa[i - 1] + k]) ? p : ++p;
m = p;
}
for (int i = 1; i <= len; ++i) rnk[sa[i]] = i;
for (int i = 1, k = 0; i <= len; ++i) {
if (k) --k;
while (ch[i + k] == ch[sa[rnk[i] - 1] + k]) ++k;
ht[rnk[i]] = k;
P[i][0] = i;
}
ht[0] = 0x3f3f3f3f;
for (int i = 1; i < 20; ++i)
for (int j = 1; j <= len - (1 << i) + 1; ++j)
P[j][i] = (ht[P[j][i - 1]] < ht[P[j + (1 << i - 1)][i - 1]] ? P[j][i - 1] : P[j + (1 << i - 1)][i - 1]); } long long res = 0; void div(int l, int r) {
if( l > r ) return;
int ps = que( l , r );
res += 1ll * ht[ps] * ( ps - l + 1 ) * ( r - ps + 1 );
div( l , ps - 1 ) , div( ps + 1 , r );
} void main() {
// freopen("1.in","r",stdin);
scanf("%s", ch + 1);
init();
div( 1 , len );
cout << 1ll * ( len - 1 ) * ( len + 1 ) * len / 2 - 2 * res << endl;
}
}
int main() {
wtf::main();
}

最新文章

  1. Java Serializable系列化与反系列化
  2. /proc/interrupts 统计2.6.38.8与3.10.25差异
  3. canvas 实现 柱状图
  4. Top 20 Java Libries Used by Github&#39;s Most Popular Java Projects
  5. UIView-4-EventForViews(在view上加入button时候的事件处理)
  6. richTextBoxFontClass
  7. 我的iphone6退货之路
  8. 【Linux】常用命令 lsof查看打开的文件
  9. Servlet页面间对象传递的方法
  10. 为什么ABAP开发者需要使用面向对象技术?
  11. awk 手册
  12. BZOJ_3239_Discrete Logging_BSGS
  13. maven 当两个工程合并后 他的classpath也合并了
  14. hdu-1814(2-sat)
  15. 【JMeter】【性能测试】配置元件
  16. 解决Mac OS下安装MyEclipse报错:Your system does not have sufficient memory to support MyEclipse
  17. codeforces水题100道 第二十六题 Codeforces Beta Round #95 (Div. 2) A. cAPS lOCK (strings)
  18. undefined和“undefined”
  19. RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
  20. TW实习日记:第八天

热门文章

  1. 脚本注入1(boolean&amp;&amp;get)
  2. 【数据结构与算法Python版学习笔记】目录索引
  3. Java:ArrayList类小记
  4. UltraSoft - Beta - 设计与计划
  5. [Beta]the Agiles Scrum Meeting 10
  6. seata序列化日期类型出错
  7. 2021.10.7 NKOJ周赛总结
  8. linux rtl8188eu ap模式 密码错误 disassoc&amp;#160;reason&amp;#160;code(8)
  9. DP秒思维
  10. udev 使用方法