P3604 美好的每一天

lxl 的题还真是毒瘤…

题意大概是 l ~ r 区间有多少个回文子串…
然后考虑怎么做…数字的出现次数怎么看都是和莫队没有关系的。
不难想到,想要一段区间回文,必须小于等于一个有奇数次出现的字母。

那么考虑 -> 这题

你就有想法了,就是把一个区间异或起来。剩下的值就是奇数次出现的。

那么又可以想到 一共有 26 个字母 你可以把每个字母压成 \(2^0\) ~ \(2^{25}\)

这样就可以当做数值来做了。

问题1:怎么求区间异或和( O(1) 的 复杂度 )

问题2: 怎么莫队(区间的当然和平常的莫队不太一样)

请读者 仔细思考以上问题再继续下翻。

异或和是可以通过像前缀和的方式求的
前缀和的预处理 \(sum_i\) = \(sum_{i-1}\) + \(a_i\)
查询 \(sum_{i=l}^{r}\) \(sum_r\) - \(sum_{l-1}\)

异或前缀和也是可以做的 类似正常的前缀和
\(sum_i\) = \(sum_{i-1}\) ^ \(a_i\)
\(\oplus_{i=l}^{r}\) = \(sum_r\) ^ \(sum_{l-1}\)

然后考虑怎么莫队
当你把 \(sum_i\) 设成了 1 到 i 的 异或和
你需要做的就是求这个区间的两个数的异或和是否由二进制位组成。
然后这个问题就转化成 \(\mathfrak{0(26n)}\) 的预处理了
你只需要算出来对它有贡献的且序列里有的然后放到 \(vector\)
每次移动左右指针的时候修改的复杂度就会降低
我个人建议把 所有的 l 都 减 1 这样操作起来会方便点 而且 r 到 r 这个区间也算贡献的。

给出两道弱化版的题 建议做好了再做这题。

https://www.luogu.org/problem/P4462
https://www.luogu.org/problem/CF617E

我在这道题上栽了个坑… 其他题我也没注意到这个问题 Ins 和 Del 操作建议放在一起
不放在一起就会相互影响 这样答案可能不对
这题的整体复杂度是 \(\mathfrak{0(26 n\sqrt{n})}\)

code

#pragma GCC optimize("Ofast")
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std ;
const int N = 6e4 + 5 ;
struct node {
	int l , r , id , bl ;
	bool operator < (const node & c) const {
		return bl < c.bl || (bl == c.bl && r < c.r) ;
	}
} q[N] ;
int n , m , a[N] , Ans[N] , ans = 0 ;
char s[N] ;
vector < int > v[N] ;
unsigned short cnt[1 << 26] ;
bool vis[1 << 26] ;
inline void Ins(int x) { ans += cnt[a[x]] ++ ; for( int & pt : v[x] ) ans += cnt[pt] ; }
inline void Del(int x) { ans -= -- cnt[a[x]] ; for( int & pt : v[x] ) ans -= cnt[pt] ; }
signed main() {
	scanf("%d %d %s" , & n , & m , s + 1) ;
	int unt = n / sqrt(m * 0.9) ;
	for(register int i = 0 ; i <= n ; i ++) a[i] = i ? a[i - 1] ^ (1 << (s[i] - 'a')) : 0 , vis[a[i]] = 1 ;
	for(register int i = 0 ; i <= n ; i ++)
		for(register int j = 0 ; j <= 25 ; j ++) if(vis[a[i] ^ (1 << j)]) v[i].emplace_back(a[i] ^ (1 << j)) ;
	for(register int i = 1 ; i <= m ; i ++) scanf("%d %d" , & q[i].l , & q[i].r) , q[i].bl = q[i].l / unt , q[i].id = i ;
	sort(q + 1 , q + m + 1) ;
	int l = q[1].l , r = q[1].l - 1; cnt[a[l - 1]] ++ ;
	for(register int i = 1; i <= m; i ++) {
			while(l > q[i].l) Ins(-- l - 1) ;
			while(r < q[i].r) Ins(++ r) ;
			while(l < q[i].l) Del(l ++ - 1) ;
			while(r > q[i].r) Del(r --) ;
			Ans[q[i].id] = ans ;
	}
	for(register int i = 1 ; i <= m ; i ++) printf("%d\n" , Ans[i]) ;
	return 0 ;
}
posted @ 2019-10-09 16:08  _Isaunoya  阅读(184)  评论(0编辑  收藏  举报