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;
}
posted @ 2023-12-05 08:35  cxqghzj  阅读(11)  评论(0编辑  收藏  举报