「NOI2023」字符串

题解


我们先把正串和反串拼起来建 sa, 然后我们发现就是合法的条件就是:

\[rk_i < R(rk_{i+2l-1}) \]

\[lcp(rk_i, R(rk_{i+2l-1})) < l \]

我们发现第二个条件不好做,于是考虑给他容斥掉。

思考第一个条件,我们可以按照 \(rk\) 从大到小来加入,同时维护两个树状数组分别存奇数和偶数的,假如说加入的是反串部分,就按照奇偶性加入不同的树状数组,如果是正串就做一次询问。

这样就可以求出来满足第一个条件的答案。

然后思考现在我们是要求:

\[rk_i < R(rk_{i+2l-1}) \]

\[s[i , i+l - 1] = R(s[i + l, i + 2l - 1]) \]

我们发现第二个条件就是以 \(i+l-1\)\(i+l\) 为回文中心的回文串。

于是我们就可以利用 manacher 算法,跑出来每个满足最左边的 \(rk\) 小于最右边的 \(rk\) 的极长回文串,为什么只需要关注最左边和最右边的 \(rk\) 呢?因为中间的大小关系也一定是最左边和最右边的,因为这个是回文串,中间的相同部分不影响比较。

然后我们从编号从大到小加入,现在条件就形如单点加,然后询问区间满足条件的数的和,再写一个树状数组就行了。

点击查看代码
//奇跡を信じて,願いを叶えて
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define uint unsigned long long
#define Air
namespace io{
	inline int read(){
		int x; cin >> x; return x;
	}
	inline void write(int x){
		if(x < 0){putchar('-'); x = -x;}
		if(x >= 10){write(x / 10);}
		putchar(x % 10 + '0');
	}
}
using namespace io;
int n, q;
const int N = 2e5 + 10;
string t, s;
int rk[N], sa[N], tmp[N];
void get_sa(){
	for(int i = 1; i <= n * 2 + 1; i++){
		rk[i] = s[i];
		sa[i] = i;  
		// cerr << rk[i] << '\n';
	}
	for(int len = 1; len <= n * 2 + 1; len *= 2){
		sort(sa + 1, sa + 1 + n * 2 + 1, [=](int x, int y){
			return (rk[x] != rk[y]) ? (rk[x] < rk[y]) : (rk[x + len] < rk[y + len]);
		});
		for(int i = 1; i <= n * 2 + 1; i++){
			tmp[i] = rk[i];
		}
		int tot = 0;
		for(int i = 1; i <= n * 2 + 1; i++){
			if(tmp[sa[i]] != tmp[sa[i - 1]] || tmp[sa[i] + len] != tmp[sa[i - 1] + len]){
				tot ++;
			}
			rk[sa[i]] = tot;
		}
	}
}
struct Bit{
	int tr[N];
	#define lowbit(x) (x & (-x))
	void add(int x, int c){
		for(; x < N; x += lowbit(x)){
			tr[x] += c;
		}
	}
	int query(int x){
		int ans = 0;
		for(; x; x -= lowbit(x)){
			ans += tr[x];
		}
		return ans;
	}
	int ask(int l, int r){
		if(l > r) return 0;
		return query(r) - query(l - 1);
	}
}bt1, bt2, bt3;
struct Data{
	int val, id;
};
struct Node{
	int pos, val;
};
int ans[N];
vector<Data> que[N];
vector<Node> chg[N];
string ss;
int len[N];
void manacher(){
	len[0] = 0;
    int mid = 0, r = 0;
    for(int i = 1; i <= n * 2; i++){
        if(i <= r){
            len[i] = min(len[mid * 2 - i], r - i + 1);
        }
        while(ss[i + len[i]] == ss[i - len[i]]) len[i] ++;
        if(r < i + len[i] - 1){
            r = i + len[i] - 1;
            mid = i;
        }
    }
}
void work(){
	n = read();
	q = read();
	cin >> t; t = ' ' + t;
	ss = " #";
	s = t;
	s += (char)('z' + 1);
	memset(bt1.tr, 0, sizeof bt1.tr);
	memset(bt2.tr, 0, sizeof bt2.tr);
	memset(bt3.tr, 0, sizeof bt3.tr);
	memset(sa, 0, sizeof sa);
	memset(rk, 0, sizeof rk);
	memset(tmp, 0, sizeof tmp);
	memset(len, 0, sizeof len);
	for(int i = 1; i <= n; i++){
		s += t[n - i + 1];
		ss += t[i]; ss += '#';
		
	}
	for(int i = 0; i <= n; i++){
		que[i].clear();
		chg[i].clear();
	}
	// cerr << ss << '\n';
	get_sa();
	// cerr << "!!" << rk[n + 1] << '\n';
	for(int i = 1; i <= q; i++){
		int id = read();
		ans[i] = 0;
		que[id].push_back({read(), i});
	}
	for(int i = n * 2 + 1; i >= 1; i--){
		int pos = sa[i];
		// cerr << pos << '\n';
		if(pos == n + 1) continue;
		// cerr << pos << '\n';
		if(pos <= n){
			for(auto y: que[pos]){
				if(pos & 1)
					ans[y.id] += bt2.ask(pos, pos + y.val * 2 - 1);
				else
					ans[y.id] += bt1.ask(pos, pos + y.val * 2 - 1);
			} 
		}
		else{
			pos = 2 * n + 2 - pos;
			if(pos & 1){
				bt1.add(pos, 1);
			}
			else{
				bt2.add(pos, 1);
			}
		}
	}
	manacher();
	// return ;
	// cerr << ans[1] << '\n';
	for(int i = n - 1; i >= 1; i--){
		int pos = i * 2 + 1;
		if(len[pos] == 1) continue;
		// cerr << len[pos] << '\n';
		int tl = pos - len[pos] + 1, tr = pos + len[pos] - 1;
		tl ++; tr --;
		tl /= 2; tr /= 2;
		// cerr << pos << ' ' << len[pos] << ' ' << tl << ' ' << tr << '\n';
		if(rk[tl] < rk[n * 2 + 2 - tr]){
			//这个时候才会有修改
			chg[i].push_back({i, 1});
			chg[tl - 1].push_back({i, -1});
		}
	}
	// return ;
	for(int i = n; i >= 1; i--){

		for(auto y: chg[i]){
			bt3.add(y.pos, y.val);
		}
		for(auto y: que[i]){
			ans[y.id] -= bt3.ask(i, i + y.val - 1);
		}
	}
	for(int i = 1; i <= q; i++){
		cout << ans[i] << '\n';
	}
}
signed main() {
#ifndef Air
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	read();
	int TCS = read();
	while(TCS --){
		work();
	}	
	return 0;
}
posted @ 2026-06-16 19:11  Air2011  阅读(13)  评论(0)    收藏  举报