E – Adnan and the Burned drivers题目:
给出一个长度为1e5的字符串,有1e5次操作。
操作1:修改一个字符串里的某个字符。操作2:询问字符串的\([l, r]\)是否为回文子串。
思路:
对于一个字符串快速判断是否为回文串,可以用字符串哈希通过判断正反哈希值是否相等,在\(O(logn)\)的时间内解决该问题。但是本题有一个问题是带修,那么我们可以考虑用数据结构来维护这个带修的过程。查询哈希值的过程就可以看做是一个区间求和问题,修改字符就是单点修改问题。要注意的是,要维护一个正方向的哈希值和一个反方向的哈希值。
实现:
关于字符串哈希,用unsigned long long可以自动取模,基准数可以是131或者27。
在线段树维护区间和的时候,需要注意左移和右移。
#include using namespace std;const int N = 100005;typedef unsigned long long ull;char str[N];ull bash[N * 4];int n, m;ull seg1[N * 4], seg2[N * 4];void pushup(int k, int l, int r){ int mid = l + r >> 1; seg1[k] = (seg1[k << 1] * bash[r - mid] + seg1[k << 1 | 1]); seg2[k] = (seg2[k << 1] + seg2[k <> 1; if(x <= mid) update(k << 1, l, mid, x, val); else update(k <> 1; if(qr <= mid) return query(k << 1, l, mid, ql, qr, q); else if(mid < ql) return query(k << 1 | 1, mid + 1, r, ql, qr, q); else if(q == 1) return query(k << 1, l, mid, ql, mid, q) * bash[qr - mid] + query(k << 1 | 1, mid + 1, r, mid + 1, qr, q); else return query(k << 1, l, mid, ql, mid, q) + bash[mid - ql + 1] * query(k << 1 | 1, mid + 1, r, mid + 1, qr, q);}void init(){ bash[0] = 1; for(int i = 1; i <= 4e5; i ++) bash[i] = bash[i - 1] * 131;}void solve(){ memset(seg1, 0, sizeof seg1); memset(seg2, 0, sizeof seg2); scanf("%d%d", &n, &m); scanf("%s", str + 1); for(int i = 1; i > 1; ull q, p; if((y - x + 1) & 1) //区间长度为奇数 q = query(1, 1, n, x, mid - 1, 0), p = query(1, 1, n, mid + 1, y, 1); else //偶数 q = query(1, 1, n, x, mid, 0), p = query(1, 1, n, mid + 1, y, 1); if(q == p) puts("Adnan Wins"); else puts("ARCNCD!"); } } }}int main(){ init(); int _; scanf("%d", &_); while(_--) solve();}