连NOI Day1T1都不会做。。。看了题解都写不出来还要抄Claris的代码。。

题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117

(bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4650

(uoj)http://uoj.ac/problem/219

题解:

\(f[i]\)表示以\(i\)结束的\(AA\)型子串个数,\(g[i]\)表示以\(i\)开始的\(AA\)型子串个数

怎么求\(f,g\)?

打破思维定势,谁说必须要一个一个求呢

分长度来求

枚举长度\(L\), 处理所有长度为\(2L\)的\(AA\)型子串对\(f\)和\(g\)的贡献

如果每隔\(L\)的长度放一个打点计时器,呸,关键点

那么任何\(AA\)型子串都会经过两个相邻关键点

首先肯定要满足这两个关键位置上的字符一样

在这个基础上求出往前往后最多多少个一样的

这个就转化成了LCP和LCS问题,并且两个相邻关键点对\(f,g\)数组的影响是区间+1,使用差分前缀和解决

然后推一推就行了,注意+1-1不要推错

时间复杂度为调和级数,\(O(n\log n)\)

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define llong long long
using namespace std; const int N = 1<<15;
const int LGN = 15;
const int S = 26;
int log2[N+3];
struct SparseTable
{
int n;
int str[N+3];
int rk[N+3];
int tmp[N+3];
int height[N+3];
int h[N+3];
int sa[N+3];
int wb[N+3];
int mini[N+3][LGN+3];
void get_sa()
{
int *x = rk,*y = tmp;
for(int i=0; i<=S; i++) wb[i] = 0;
for(int i=1; i<=n; i++) wb[x[i]=str[i]]++;
for(int i=1; i<=S; i++) wb[i] += wb[i-1];
for(int i=n; i>=1; i--) sa[wb[x[i]]--] = i;
int s = S,p = 0;
for(int j=1; p<n; j<<=1)
{
p = 0;
for(int i=n-j+1; i<=n; i++) y[++p] = i;
for(int i=1; i<=n; i++) if(sa[i]>j) y[++p] = sa[i]-j;
for(int i=1; i<=s; i++) wb[i] = 0;
for(int i=1; i<=n; i++) wb[x[y[i]]]++;
for(int i=1; i<=s; i++) wb[i] += wb[i-1];
for(int i=n; i>=1; i--) sa[wb[x[y[i]]]--] = y[i];
swap(x,y);
p = 1; x[sa[1]] = 1;
for(int i=2; i<=n; i++) x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p;
s = p;
}
for(int i=1; i<=n; i++) rk[sa[i]] = i;
for(int i=1; i<=n; i++)
{
h[i] = h[i-1]==0 ? 0 : h[i-1]-1;
while(i+h[i]<=n && sa[rk[i-1]]+h[i]<=n && str[i+h[i]]==str[sa[rk[i]-1]+h[i]])
{
h[i]++;
}
}
for(int i=1; i<=n; i++) height[i] = h[sa[i]];
for(int i=1; i<=n; i++) mini[i][0] = height[i];
for(int j=1; j<=LGN; j++)
{
for(int i=1; i+(1<<j)-1<=n; i++)
{
mini[i][j] = min(mini[i][j-1],mini[i+(1<<j-1)][j-1]);
}
}
}
int querymin(int lb,int rb)
{
int g = log2[rb-lb+1];
return min(mini[lb][g],mini[rb-(1<<g)+1][g]);
}
int LCP(int x,int y)
{
if(x==y) return n-x+1;
if(rk[x]>rk[y]) swap(x,y);
return querymin(rk[x]+1,rk[y]);
}
void clear()
{
for(int i=1; i<=n; i++) str[i] = rk[i] = tmp[i] = height[i] = h[i] = sa[i] = wb[i] = 0;
for(int i=1; i<=n; i++)
{
for(int j=0; j<=LGN; j++)
{
mini[i][j] = 0;
}
}
}
} s1,s2;
llong f[N+3];
llong g[N+3];
char a[N+3];
int n; int LCP(int x,int y) {return s1.LCP(x,y);}
int LCS(int x,int y) {return s2.LCP(n+1-x,n+1-y);} void preprocess()
{
log2[1] = 0; for(int i=2; i<=N; i++) log2[i] = log2[i>>1]+1;
} void clear()
{
s1.clear(); s2.clear();
for(int i=0; i<=n+1; i++) a[i] = 0,f[i] = g[i] = 0ll;
} int main()
{
preprocess();
int T; scanf("%d",&T);
while(T--)
{
scanf("%s",a+1); n = strlen(a+1); for(int i=1; i<=n; i++) a[i]-=96;
for(int i=1; i<=n; i++) s1.str[i] = a[i]; s1.n = n;
s1.get_sa();
for(int i=1; i<=n; i++) s2.str[i] = a[n+1-i]; s2.n = n;
s2.get_sa();
for(int i=1; i+i<=n; i++)
{
for(int j=i+i; j<=n; j+=i)
{
if(a[j]==a[j-i])
{
int lb = j-LCS(j,j-i)+1,rb = j+LCP(j,j-i)-1;
lb = max(lb+i-1,j); rb = min(rb,j+i-1);
if(lb<=rb)
{
f[lb]++; f[rb+1]--;
g[lb-i-i+1]++; g[rb+1-i-i+1]--;
}
}
}
}
for(int i=1; i<=n; i++) f[i] += f[i-1],g[i] += g[i-1];
llong ans = 0ll;
for(int i=1; i<n; i++)
{
llong tmp = f[i]*g[i+1];
ans += tmp;
}
printf("%lld\n",ans);
clear();
}
return 0;
}

UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)的更多相关文章

  1. UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...

  2. UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...

  3. [NOI2016]优秀的拆分 后缀数组

    题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...

  4. BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)

    BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...

  5. Luogu P1117 [NOI2016]优秀的拆分

    题目链接 \(Click\) \(Here\) 这题质量不错,就是暴力分有点足\(hhhhhhhh\),整整有\(95\)分. (搞得我写完暴力都不想写正解直接理解思路之后就直接水过去了\(QwQ\) ...

  6. BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组

    我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...

  7. BZOJ 4453: cys就是要拿英魂![后缀数组 ST表 单调栈类似物]

    4453: cys就是要拿英魂! Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 90  Solved: 46[Submit][Status][Discu ...

  8. bzoj 3796: Mushroom追妹纸【二分+后缀数组+st表】

    把三个串加上ASCII大于z的分隔符连起来,然后求SA 显然每个相同子串都是一个后缀的前缀,所以枚举s1的每个后缀的最长和s2相同的前缀串(直接在排序后的数组里挨个找,最近的两个分别属于s1和s2的后 ...

  9. [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分

    [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...

随机推荐

  1. pair类型 这次遇到了,记录下来,方便彼此xue习

    首先,这个pair类型是在头文件utility.h中. 一个piar保存两个数据成员,是用来生成特定类型的模板,当创建一个pair时,我们必须提供两个类型名,pair的数据成员将具有对应的类型,两个类 ...

  2. yii引入js文件

    作者:zccst 四.在视图层(../views/..)添加CSS文件或JavaScript文件 Yii::app()->clientScript->registerScriptFile( ...

  3. js中 if不判断解决方式

    $(function() { $("#number").blur(function() { var number = $('#number').val(); var num = $ ...

  4. 【poj1995】快速幂

    题目大意 求a^b %p 1≤a,b,p≤10^9 思路 时间O(10^9)一定会爆T,采用数学方法+位运算,得到O(log b)的快速幂算法 代码 #include<cstdio> #i ...

  5. JSP页面中path和basepath的含义

    今天在看代码时,发现程序使用了 request.getScheme() .不明白是什么意思,查了一下.结果整理如下: 1.request.getScheme() 返回当前链接使用的协议:一般应用返回h ...

  6. Oracle 关于oracle自带的行转列函数

    前言: 环境是java+hibernate+oracle11g 目标是将某表中根据id分组后将name字段的值拼接到一列中,且用“,“进行分割 试过用 wm_concat() 结合 group by ...

  7. 网站html代码解析

    1.什么是HTML文件?HTML中文叫做“超文本标记语言”,一个HTML文件不仅包含文本内容,还包含一些标记,一个HTML文件的后缀名是.htm或者是.html.用文本编辑器(Dreamweaver) ...

  8. Android嵌入式(初稿)--路漫漫其修远兮,吾将上下而求索

  9. 【Oracle】进入sqlplus 删除键backspace时出现^H

    当oracle进入sqlplus后,输入命令时候出现错误,我们按平时的习惯使用backspace键删除错误信息,此时会出现^H 解决办法:进入sqlplus之前,使用stty erase '^H'命令 ...

  10. 《Linux程序设计》笔记(一)入门

    1. 头文件 使用-I标志来包含头文件. gcc -I/usr/openwin/include fred.c 2. 库文件 通过给出 完整的库文件路径名 或 用-l标志 来告诉编译器要搜索的库文件. ...