P3604 美好的每一天莫队 + 思维

tech2023-05-26  61

传送门

真是个练习卡常的好题呢

题意:给定一个串,让后每次给定一个区间,问这个区间的子区间为回文子区间有多少个。这里的回文串是区间内字母重排之后是回文串即可。 既然重排之后是回文就行,那么只需要知道这个区间内奇数的个数就行了。字母只有26个,那么可以状态压缩一下,用一个数组记录一下每一位即可。 既然只关心个数的奇偶,那么可以用异或来判断。区间异或显然可以转换成前缀异或,假设当前区间为 [ x , y ] ,那么需要选择两个数 i j ∈ [ x - 1 , y ] ,使其异或值为 0 (此时奇数个数为0) 或者2 ^ n (此时为奇数有一个) 即可。而我假设 cnt 为记录的前缀异或和数量的数组,加入当前要加的数为 a [ x ] ,那么可以将 a [ x ] ^ ( 2 ^ n , n ∈ [ 0 , 25 ] ) 得到相应的数,加上其个数即可,让后再加上跟自身相同的数个数 ( 也就是异或结果为0 )。这样就解决了。

我不怎么喜欢卡常好吧是我不会卡常 ,能过了就行了 ~ ~ ~

#pragma GCC optimize(2) #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<map> #include<cmath> #include<cctype> #include<vector> #include<set> #include<queue> #include<algorithm> #include<sstream> #define X first #define Y second #define L (u<<1) #define R (u<<1|1) #define Mid (tr[u].l+tr[u].r>>1) #define Len(u) (tr[u].r-tr[u].l+1) #define pb push_back #define mk make_pair #define re register #define il inline using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N=60010,mod=1e9+7,INF=0x3f3f3f3f; const double eps=1e-6; int n,m; int ans[N],cnt[(1<<26)+3],id[N],now,a[N]; char s[N]; struct Node { int l,r; int id; }q[N]; il bool cmp(Node a,Node b) { return id[a.l]^id[b.l]? a.l<b.l : ((id[a.l]&1)? a.r<b.r : a.r>b.r); } void del(int x) { cnt[x]--; now-=cnt[x];//把与它异或为0的去掉 for(re int i=0;i<26;i++) now-=cnt[x^(1<<i)]; } void add(int x) { now+=cnt[x]; cnt[x]++;//先把异或为0的加上 for(re int i=0;i<26;i++) now+=cnt[x^(1<<i)]; } int main() { // ios::sync_with_stdio(false); // cin.tie(0); scanf("%d%d",&n,&m); cin>>s+1; int se=sqrt(n); for(re int i=1;i<=n;i++) { int x=s[i]-'a'; a[i]=1<<x; a[i]^=a[i-1]; id[i]=(i-1)/se+1; } for(re int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; sort(q+1,q+1+m,cmp); int l=1,r=0; for(re int i=1;i<=m;i++) { int lx=q[i].l-1,rx=q[i].r; while(l<lx) del(a[l++]); while(l>lx) add(a[--l]); while(r<rx) add(a[++r]); while(r>rx) del(a[r--]); ans[q[i].id]=now; } for(re int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; } /* */
最新回复(0)