P3604 美好的每一天
题意
给定一个字符串 \(S\),每次区间查询 \(l, r\) 中有多少子区间重排可以形成回文串。
Sol
莫队板子题。
首先套路地,状压 \(26\) 个字母,然后做异或前缀和。
问题变为当前区间内有多少个 \([x, y]\) 使得 \(s[y] \oplus s[x - 1]\) 有或者没有一位是 \(1\)。
考虑如何扩展一个区间,利用异或的性质。当转移运算是异或地时候,完全可以考虑枚举答案以减少枚举地数量级。
我们考虑枚举哪一位上是 \(1\)。发现可以在 \(O(26)\) 的时间内完成扩展区间。
套上莫队板子,这道题就做完了。
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
string read_() {
string ans;
char c = getchar();
while (c < 'a' || c > 'z')
c = getchar();
while (c >= 'a' && c <= 'z') {
ans += c;
c = getchar();
}
return ans;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
const int N = 6e4 + 5;
array <int, N> s;
namespace Cpt {
int bsi;
struct Node {
int x, y, id;
bool operator < (const Node &t) const {
if (x / bsi != t.x / bsi) return x < t.x;
return (x / bsi) & 1 ? y < t.y : y > t.y;
}
};
array <Node, N> qrl;
array <int, (1 << 26) + 5> cur;
int ans;
void add(int x) {
ans += cur[s[x]];
for (int i = 0; i < 26; i++)
ans += cur[s[x] ^ (1 << i)];
cur[s[x]]++;
}
void del(int x) {
cur[s[x]]--;
ans -= cur[s[x]];
for (int i = 0; i < 26; i++)
ans -= cur[s[x] ^ (1 << i)];
}
}
using Cpt::qrl; using Cpt::add; using Cpt::del;
array <int, N> ans;
int main() {
Cpt::bsi = 332;
int n = read(), m = read();
string str = " " + read_();
for (int i = 1; i <= n; i++)
s[i] = (1 << (str[i] - 'a')) ^ s[i - 1];
for (int i = 1; i <= m; i++) {
qrl[i].x = read(), qrl[i].y = read();
qrl[i].id = i;
}
sort(qrl.begin() + 1, qrl.begin() + 1 + m);
/* puts("@"); */
int l = 1, r = 0;
for (int i = 1; i <= m; i++) {
int x = qrl[i].x - 1, y = qrl[i].y;
int id = qrl[i].id;
while (l > x) l--, add(l);
while (r < y) r++, add(r);
while (l < x) del(l), l++;
while (r > y) del(r), r--;
ans[id] = Cpt::ans;
}
for (int i = 1; i <= m; i++)
write(ans[i]), puts("");
return 0;
}