2024牛客暑期多校训练营4 - J. Zero (卡常)
\(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;
}
还可以搞一下,去掉常数极大的取模运算并添加 unsigned
,速度可以再提几倍。
#include<cstdio>
using namespace std;
constexpr unsigned int N=1e5+5,P=998244353;
unsigned int n,k;
char str[N];
unsigned int qsum[N]; //the number of '?' in str[1,i]
unsigned int binpow[N],powk[N],invbinpow[N];
unsigned int quick_pow(unsigned long long x,unsigned int y)
{
unsigned long long res=1;
while(y)
{
if(y&1) res=res*x%P;
x=x*x%P;
y>>=1;
}
return (unsigned int)res;
}
inline unsigned int inv(unsigned int x)
{
return quick_pow(x,P-2);
}
int main()
{
// freopen("zero.in","r",stdin);
// freopen("zero.out","w",stdout);
scanf("%u%u",&n,&k);
scanf("%s",str+1);
binpow[0]=1;
invbinpow[0]=inv(binpow[0]);
for(unsigned 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);
}
__int128 ans=0;
for(unsigned int l=1;l<=n;l++)
{
if(str[l]=='0') continue;
for(unsigned int r=l;r<=n&&str[r]!='0';r++)
{
unsigned int q=qsum[r]-qsum[l-1];
ans+=(unsigned long long)invbinpow[q]*powk[r-l+1];
}
}
printf("%llu\n",(unsigned long long)(ans%P));
return 0;
}