[DS记录]P3604 美好的每一天

command_block

2021-06-28 08:44:57

Personal

**题意** ; 给出一个长度为 $n$ 的字符串 $S$, $q$ 次询问 $S[l,r]$ 有多少个子串在重排后可能形成回文串。 $n,q\leq 6\times 10^4$ ,字符集为小写英文字母。时限$\texttt{3s}$ ,空限$\texttt{160M}$。 ------------ 考虑一个字符串重排后可能形成回文串的充要条件 : 出现次数为奇数的字符个数 $\leq 1$。 记 $w_i$ 表示前缀 $S[1,i]$ 的各个字符出现次数的奇偶性,压成一个 $\Sigma$ 位的二进制数。 使用莫队,当区间 $[l+1,r]\rightarrow [l,r]$ 时,答案变化量为 : $$\sum\limits_{i=l}^r\Big[\big|w_{l-1}{\ \rm xor\ } w_{i}\big|\leq 1\Big]$$ 满足 $|x|\leq 1$ 的二进制数只有 $O(\Sigma)$ 个。再用桶维护每个二进制数出现的次数即可支持 $O(\Sigma)$ 查询。 这个桶的大小为 $O(2^{\Sigma})$ ,有点太大了。本质不同的修改只有 $O(n)$ 种,询问只有 $O(n\Sigma)$ 种,使用离散化即可节省空间。 接着使用二次离线莫队。 考虑如何 $O(1)$ 查询。我们将修改向能贡献到的查询连边,可以证明去重之后,每个修改只会对应 $O(\Sigma)$ 个查询。 这样,我们让修改主动贡献,即可 $O(\Sigma)$ 修改,$O(1)$ 查询。 复杂度 $O(n\sqrt{n}+n\Sigma\log n)$。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #include<cmath> #define pb push_back #define ll long long #define MaxN 60500 using namespace std; char str[MaxN]; struct Data{int l,r,p;}; vector<Data> b[MaxN]; int n,w[MaxN],x[MaxN],o[MaxN],to[MaxN][30],top[MaxN],sl[MaxN]; ll ans[MaxN]; void calc() { w[0]=0; for (int i=1;i<=n;i++) x[i]=w[i]=w[i-1]^(1<<(str[i]-'a')); x[n+1]=0;sort(x+1,x+n+2); int tn=unique(x+1,x+n+2)-x-1; for (int i=1;i<=tn;i++){ to[i][top[i]=0]=i; for (int k=0;k<26;k++){ int tc=x[i]^(1<<k) ,tp=lower_bound(x+1,x+tn+1,tc)-x; if (x[tp]==tc)to[i][++top[i]]=tp; } } w[0]=sl[0]=1; for (int k=0;k<=top[1];k++)o[to[1][k]]++; for (int i=1;i<=n;i++){ w[i]=lower_bound(x+1,x+tn+1,w[i])-x; for (int k=0;k<=top[w[i]];k++)o[to[w[i]][k]]++; sl[i]=o[w[i]]; for (int j=0;j<b[i].size();j++){ Data now=b[i][j];ll buf=0; for (int k=now.l;k<=now.r;k++) buf+=o[w[k-1]]-sl[k-1]; if (now.p>0)ans[now.p]+=buf; else ans[-now.p]-=buf; } }for (int i=1;i<=n;i++){b[i].clear();o[i]=0;} } Data s[MaxN];int BS,m; bool cmp(const Data &A,const Data &B) {return A.l/BS==B.l/BS ? ((A.l/BS)&1 ? A.r<B.r : A.r>B.r) : A.l<B.l;} int main() { scanf("%d%d%s",&n,&m,str+1); for (int i=1;i<=m;i++){ scanf("%d%d",&s[i].l,&s[i].r); s[i].p=i; } BS=sqrt(n+1);sort(s+1,s+m+1,cmp); int l=1,r=0; for (int i=1;i<=m;i++){ if (s[i].l<l){b[r].pb((Data){s[i].l,l-1,s[i].p});l=s[i].l;} if (r<s[i].r)r=s[i].r; if (l<s[i].l){b[r].pb((Data){l,s[i].l-1,-s[i].p});l=s[i].l;} if (s[i].r<r)r=s[i].r; } calc(); reverse(str+1,str+n+1); l=n+1;r=n; for (int i=1;i<=m;i++){ swap(s[i].l,s[i].r); s[i].l=n-s[i].l+1;s[i].r=n-s[i].r+1; if (r<s[i].r)r=s[i].r; if (s[i].l<l){b[r].pb((Data){s[i].l,l-1,s[i].p});l=s[i].l;} if (s[i].r<r)r=s[i].r; if (l<s[i].l){b[r].pb((Data){l,s[i].l-1,-s[i].p});l=s[i].l;} }calc(); for (int i=1;i<=m;i++)ans[s[i].p]+=ans[s[i-1].p]; for (int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; } ```