「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;
}

浙公网安备 33010602011771号