\(O(N^2)\) AC。
輸入後預處理 ?
數量的字首和。
雙層迴圈找所有的區間 \([l,r]\) 使區間內沒有 \(0\),找到以後直接用逆元+快速冪求 \(\frac{(r-l+1)^k}{2^{sum_{r}-sum_{l-1}}}\),最後累加和。
因為資料過水,這樣已經能 AC 了。
#include<cstdio>
using namespace std;
const int N=1e5+5,P=998244353;
int n,k;
char str[N];
int qsum[N]; //the number of '?' in str[1,i]
int quick_pow(long long x,int y)
{
long long res=1;
while(y)
{
if(y&1) res=res*x%P;
x=x*x%P;
y>>=1;
}
return (int)res;
}
inline int inv(int x)
{
return quick_pow(x,P-2);
}
int main()
{
scanf("%d%d",&n,&k);
scanf("%s",str+1);
for(int i=1;i<=n;i++)
qsum[i]=qsum[i-1]+(str[i]=='?');
long long ans=0;
for(int l=1;l<=n;l++)
{
if(str[l]=='0') continue;
for(int r=l;r<=n;r++)
{
if(str[r]=='0') break;
int q=qsum[r]-qsum[l-1];
ans=(ans+1ll*inv(quick_pow(2,q))*quick_pow(r-l+1,k))%P;
}
}
printf("%lld\n",ans);
return 0;
}
然而,還沒有到極限。
預處理出所有的 \(2^i \bmod 998244353, i \in [0,n]\) 和 \(i^k \bmod 998244353, i \in [0,n]\),這樣過程中就可以不用重複計算。
但還不夠,inv(quick_pow(2,q))
同樣可以預處理,所以在前面同時預處理 \((2^i)^{-1} \bmod 998244353\),這樣過程中也不用計算逆元了。
#include<cstdio>
using namespace std;
const int N=1e5+5,P=998244353;
int n,k;
char str[N];
int qsum[N]; //the number of '?' in str[1,i]
int binpow[N],powk[N],invbinpow[N];
int quick_pow(long long x,int y)
{
long long res=1;
while(y)
{
if(y&1) res=res*x%P;
x=x*x%P;
y>>=1;
}
return (int)res;
}
inline int inv(int x)
{
return quick_pow(x,P-2);
}
int main()
{
// freopen("zero.in","r",stdin);
// freopen("zero.out","w",stdout);
scanf("%d%d",&n,&k);
scanf("%s",str+1);
binpow[0]=1;
invbinpow[0]=inv(binpow[0]);
for(int i=1;i<=n;i++)
{
qsum[i]=qsum[i-1]+(str[i]=='?');
binpow[i]=binpow[i-1]<<1;
if(binpow[i]>=P) binpow[i]-=P;
invbinpow[i]=inv(binpow[i]);
powk[i]=quick_pow(i,k);
}
long long ans=0;
for(int l=1;l<=n;l++)
{
if(str[l]=='0') continue;
for(int r=l;r<=n;r++)
{
if(str[r]=='0') break;
int q=qsum[r]-qsum[l-1];
ans=(ans+1ll*invbinpow[q]*powk[r-l+1])%P;
}
}
printf("%lld\n",ans);
return 0;
}