题意:
给了一个长为n的数组,将数组里面的数分为任意个连续的子区间,问有多少种情况使得这些子区间的和组成回文的形式
例如:
可以有图示三种分法及原区间
题解:
要将原数组分为回文的区间,即首尾区间和对应相等,考虑从头和尾同时往上走,即前k个区间和与后k个区间和肯定也相等,自然联想到前缀和与后缀和,只要满足从前缀和里面选出的一些数与从后缀和里面选出的一些数,首尾对应相等,则一定可以对应一种区间分法使得区间和对应相等(所有相邻的前缀相减相邻的后缀相减还原出对应的区间)
例如
可以选第一个前缀与最后一个后缀,第二个前缀与倒数第二个后缀(前缀一定选在后缀前面不能有相交)
继续考虑怎么选出一些前缀后缀对应相等
因为数组元素大于等于0,可以发现前缀与后缀都是递增的,即某一段连续相等的前缀最多只有一段连续相等的后缀与其对应相等,且所有前后缀相等的区间一定是互相独立的,即可以把所有前缀后缀对应相等的区间单独拿出来计算方案数后相乘,双指针处理一下即可
注意 :中间一段可能重合,即该部分前缀对应该部分后缀,特殊处理一下
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
const ll N=2e5+5;
ll jc[100005];
ll jcm[100005];
ll ksm(ll p,ll q){
ll sum=1;
while(q!=0){
p=p%mod;
if(q%2==1){
sum=sum*p%mod;
p*=p;q/=2;
}
else{
p*=p;q/=2;
}
}
return sum;
}
ll c(ll n,ll m){
if(n==0&&m==0)return 1;
return jc[n]*jcm[m]%mod*jcm[n-m]%mod;
}
struct duqi{
ll n1,n2;
};
duqi q[N];
ll a[N];
ll sumq[N],sumh[N];
int main()
{
ll i,j,kt,jw,t,p,m,p1,r,mid,max1,l,g,n,k,min1,w1,v,l1,r1,x,y,p2,h,k1,i1,j1,sum,n1,n2;
//printf("%lld",c(0,0));
jc[0]=1;jcm[0]=1;
for(i=1;i<=100000;i++){
jc[i]=jc[i-1]*i%mod; }
jcm[100000]=ksm(jc[100000],mod-2);
for(i=99999;i>=1;i--)jcm[i]=(jcm[i+1]*(i+1))%mod;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
v=0;p=0;
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
sumq[0]=0;sumh[n+1]=0;
for(i=1;i<=n;i++)sumq[i]=sumq[i-1]+a[i];
for(i=n;i>=1;i--)sumh[i]=sumh[i+1]+a[i];
for(i=1,j=n;i<j;){
if(sumq[i]!=sumh[j]){
if(sumq[i]>sumh[j])j--;
else
i++;
}
else{
n1=0;n2=0;g=sumq[i];
while(sumq[i]==g&&i<=n){
i++;n1++;
}
while(sumh[j]==g&&j>=1){
j--;n2++;
}
//printf("%lld ",n1);
if(i>j+1)v=n1+1;
else{
q[++p].n1=n1;q[p].n2=n2;
}
}
}
for(i=1;i<=n;i++)if(a[i]!=0)break;
if(i>n){
printf("%lld\n",(ksm(2,n-1))%mod);continue;
}
k=1;
//printf("%lld ",v);
for(i=1;i<=p;i++){
sum=0;
for(j=0;j<=min(q[i].n1,q[i].n2);j++){
sum=(sum+c(q[i].n1,j)*c(q[i].n2,j)%mod)%mod;
}k=k*sum%mod;
}
sum=0;
for(i=0;i<=v;i+=2)sum=(sum+c(v,i))%mod;
printf("%lld\n",k*sum%mod);
}
}