4866: [Ynoi2017]由乃的商场之旅
Time Limit: 20 Sec Memory Limit: 164 MB
Submit: 192 Solved: 47
[Submit][Status][Discuss]
Description
由乃有一天去参加一个商场举办的游戏。商场派了一些球王排成一行。每个人面前有几堆球。说来也巧,由乃和你
一样,觉得这游戏很无聊,于是决定换一个商场。另一个商场是Deus的,他看到由乃来了,于是想出了一个更有趣
的游戏:写数据结构题这个题是这样的:
我们的情人,不过是随便借个名字,用幻想吹出来的肥皂泡,把信拿去吧,你可以使假戏成真。我本来是无病呻吟
,漫无目的的吐露爱情—现在这些漂泊不定的鸟儿有地方栖息了,你可以从信里看出来。拿去吧—由于不是出自
真心,话就说得格外动听,拿去吧,就这么办吧…由于世界会在7月20日完结,作为救世主,间宫卓司要在19日让
所有人回归天空现在已经是19日傍晚,大家集合在C栋的天台上,一共n个人在他们面前,便是终之空,那终结的天
空
回归天空是一件庄重的事情,所以卓司决定让大家分批次进行,给每个人给了一个小写字母’a’->’z’作为编号一个
区间的人如果满足他们的编号重排之后可以成为一个回文串,则他们可以一起回归天空,即这个区间可以回归天空
由于卓司是一个喜欢妄想的人,他妄想了m个区间,每次他想知道每个区间中有多少个子区间可以回归天空因为世
界末日要来了,所以卓司的信徒很多
由乃天天做数据结构已经快恶心死了,于是让您帮她做当然,您天天做数据结构题,肯定也觉得恶心死了,不过可
爱即正义!所以这个题还是要做的~
Input
第一行两个数n,m
之后一行一个长为n的字符串,代表每个人的编号
之后m行每行两个数l,r代表每次卓司妄想的区间
n,m<=60000
Output
m行,每行一个数表示答案
Sample Input
6 6
zzqzzq
1 6
2 4
3 4
2 3
4 5
1 1
Sample Output
16
4
2
2
3
1
HINT
Source
By 佚名提供
对于字母 a ,赋予权值 1 ,对于字母 b ,赋予权值 2 ,对于字母 c ,赋予权值 4 , …
那么对于原串的任意一段子区间,取 G 为区间内权值的 xor 和
这个子区间能重新排列成一个回文串,当且仅当 G=0 或 G 为 2 的幂次
用前缀后缀的思路瞎搞搞,能想出一个单次添加或删除头或尾 O(26) 的算法
结合莫队算法能搞出一个 O(26nn−−√) 的算法
然而无论怎么卡常数,这个算法就是过不去。。。。。
没有办法的,理论复杂度太差了!
下面结合平衡规划的思路,给出一个 O(n26n−−−√) 的算法
记 pre[i] 为位置 i 的前缀 xor 和
那么任意两个 pre 值的 xor 结果就能表示原串中一个子区间的信息
记 cnt[x] 为当新增一个值为 pre[x] 的端点时新增的合法区间个数
那么新增端点时可以 O(26) 更新好 cnt
记 B=26n−−−√ ,对于原串,每 B 个字符分一块
对于每个位置 i ,记 sum[i] 为位置 i 到它所在块的右端点这个区间中的合法区间个数
那么对于所有长度大于 B 的询问,它的答案分三类统计
图中 p 为左端点 L 所在块的右端点
对于 [L,p] 内部的贡献已经预处理好了
对于 [p+1,R] 的贡献可以通过莫队算法修改的时候统计好
对于横跨两段的贡献,枚举每个 [L−1,p−1] 的 pre 值,直接用当前的 cnt 即可
于是对于所有长度大于 B 的询问可以用总的 O(n26n−−−√) 解决
对于那些长度不超过 B 的询问,从左往右 O(26n) 更新 cnt 数组
在过程中利用类似上面那个办法减掉不存在的贡献即可
#include
#include
#include
#include
#include
#include
using namespace std;
const int M = 50;
const int N = 1 << 26;
const int maxn = 6E4 + 6;
typedef unsigned int u32;
typedef unsigned short u16;
struct data{
int l,r,Num; data(){}
data(int l,int r,int Num): l(l),r(r),Num(Num){}
bool operator < (const data &B) const {return r < B.r;}
};
int n,m,tot,B,pre[maxn],bel[maxn],L[M],R[M],mi[30];
u16 cnt[N]; u32 sum[maxn],Ans[maxn];
vector v[M],F[maxn],G[maxn];
inline void Add(int x) {for (int i = 0; i <= 26; i++) ++cnt[x ^ mi[i]];}
inline void Del(int x) {for (int i = 0; i <= 26; i++) --cnt[x ^ mi[i]];}
inline int getint()
{
char ch = getchar(); int ret = 0;
while (ch < '0' || '9' < ch) ch = getchar();
while ('0' <= ch && ch <= '9')
ret = ret * 10 + ch - '0',ch = getchar();
return ret;
}
inline int Get()
{
char ch = getchar();
while (ch < 'a' || 'z' < ch) ch = getchar();
return 1 << ch - 'a';
}
char s[20];
inline void Print(u32 x)
{
if (!x) {puts("0"); return;} int len = 0;
while (x) s[++len] = x % 10,x /= 10;
for (int i = len; i; i--) putchar(s[i] + '0'); puts("");
}
void Solve1()
{
u32 now = 0; Add(pre[0]);
for (int i = 1; i <= n; i++)
{
now += cnt[pre[i]];
for (int j = 0; j < F[i].size(); j++)
Ans[F[i][j].Num] += now;
for (int j = 0; j < G[i].size(); j++)
{
data d = G[i][j];
for (int k = d.l; k <= d.r; k++)
Ans[d.Num] -= cnt[pre[k]];
Ans[d.Num] -= now;
}
Add(pre[i]);
}
for (int i = 0; i <= n; i++) Del(pre[i]);
}
void Solve2()
{
for (int i = 1; i <= tot; i++)
{
if (!v[i].size()) continue;
sort(v[i].begin(),v[i].end());
int r = R[i]; u32 now = 0; Add(pre[R[i]]);
for (int j = 0; j < v[i].size(); j++)
{
data d = v[i][j];
while (r < d.r) now += cnt[pre[++r]],Add(pre[r]);
Ans[d.Num] = now + sum[d.l]; Del(pre[R[i]]);
for (int j = d.l - 1; j < R[i]; j++)
Ans[d.Num] += cnt[pre[j]]; Add(pre[R[i]]);
}
for (int j = R[i]; j <= r; j++) Del(pre[j]);
}
}
void Pre_Work()
{
mi[0] = 1; for (int i = 1; i < 26; i++) mi[i] = mi[i - 1] << 1;
for (int i = 1; i <= n; i++)
pre[i] = pre[i - 1] ^ Get(),bel[i] = (i - 1) / B + 1;
for (int i = 1; i <= bel[n]; i++)
L[i] = R[i - 1] + 1,R[i] = i * B;
tot = bel[n]; R[tot] = n;
for (int i = 1; i <= tot; i++)
{
for (int j = R[i] - 1; j >= L[i] - 1; j--)
Add(pre[j + 1]),sum[j + 1] = sum[j + 2] + cnt[pre[j]];
for (int j = R[i]; j >= L[i]; j--) Del(pre[j]);
}
for (int i = 1; i <= m; i++)
{
int l = getint(),r = getint();
if (bel[l] == bel[r])
{
F[r].push_back(data(l,r,i));
G[l - 1].push_back(data(l,r,i));
}
else v[bel[l]].push_back(data(l,r,i));
}
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
n = getint(); m = getint();
B = sqrt(26 * n); Pre_Work();
Solve1(); Solve2();
for (int i = 1; i <= m; i++) Print(Ans[i]);
return 0;
}