今天 zzq\text{zzq}zzq 在瞎写代码,首先他写了一个如下的树状数组(假设 int\text{int}int 可以存储任意大的整数):
int n,bit[N]; //N足够大
void init() {
for(int i=1;i<=n;++i) bit[i]=0;
}
void add(int x,int v) {
for(int i=x;i<=n;i+=i&-i) bit[i]+=v;
}
利用这个树状数组,他定义了一种对长度为 nnn 的数组 aaa 的变换 CCC :
void C(int*a) {
init();
for(int i=1;i<=n;++i) add(i,a[i]);
for(int i=1;i<=n;++i) a[i]=bit[i];
}
即将 aaa 变换为 aaa 数组形成的树状数组。
为了测试这个变换, zzq\text{zzq}zzq 初始了一个长度为 nnn 的数组 ppp,其中 pi=i(i∈[1,n])pi=i (i\in [1,n])pi=i(i∈[1,n]) 。接下来 zzq\text{zzq}zzq 会对 ppp 进行 kkk 次 CCC 变换。
int k; cin>>n>>k;
for(int i=1;i<=n;++i) p[i]=i;
for(int i=1;i<=k;++i) C(p);
最后 zzqzzqzzq 想要知道 ∑i=1npid(i)\sum_{i=1}^n p_id(i)∑i=1npid(i) ,其中 d(x)d(x)d(x) 为 xxx 的约数个数。
int ans=0;
for(int i=1;i<=n;++i) ans+=p[i]*divisor_count(i);
cout<<ans%998244353<<"\n";
写完这个乱七八糟的代码,zzq\text{zzq}zzq 懒得运行了,他希望你能告诉他这个代码的输出。
1≤n≤1018,1≤k≤1091\leq n\leq 10^{18},1\leq k\leq 10^91≤n≤1018,1≤k≤109
普通数论题,但在赛场上由于细节太多没能调出来。
考虑一个贡献对 (i,j)(i,j)(i,j) 产生贡献 ,设 ctz(i)\text{ctz}(i)ctz(i) 表示 iii 有 111 的最低位。
那么可以发现最高位到 ctz(i)\text{ctz}(i)ctz(i) 和 最高位 到 ctz(j)\text{ctz}(j)ctz(j) 相等。
而且 ctz(j)≤ctz(i)\text{ctz}(j)\leq \text{ctz}(i)ctz(j)≤ctz(i) ,对于等于的情况,i,ji,ji,j 相等,可以简单计算,所以只需要考虑不相等的情况。
设 w=(ctz(i),ctz(j))w=(\text{ctz}(i),\text{ctz}(j))w=(ctz(i),ctz(j)) 中 000 的个数,可以发现相当于在一个有 www 个中间点的链上行走,每次只能往后跳若干步,也可以不跳,求 kkk 步到终点的方案数。
考虑组合意义即可,插板可得 (k−1+w+1w+1)=kw+1‾(w+1)!\binom {k-1+w+1} {w+1}=\dfrac{k^{\overline {w+1}}}{(w+1)!}(w+1k−1+w+1)=(w+1)!kw+1。
那么我们直接暴力枚举 ctz(i),ctz(j),w\text{ctz}(i),\text{ctz}(j),wctz(i),ctz(j),w,剩下部分直接对奇数除法分块就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=64,mod=998244353,inv2=499122177;
long long n;
int k,t,fac[N],inv[N],f[N][N],g[N][N];
int a[10010],b[10010];
void add(int x,int t){
int tot=1;
while(x<=n){
b[x]=(b[x]+t)%mod;
x+=(x&(-x));
}
}
long long sum(long long x,long long y){
x=x%mod;y=y%mod;
return (x+y)*(y+mod-x+1)%mod*inv2%mod;
}
void solve(){
for(int i=1;i<=n;i++) a[i]=i;
for(int i=1;i<=k;i++){
for(int i=1;i<=n;i++) add(i,a[i]);
for(int i=1;i<=n;i++) a[i]=b[i],b[i]=0;
}
int ans=0;
for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) ans=(ans+a[j])%mod;
printf("%d\n",ans);
exit(0);
}
int main(){
scanf("%lld %d",&n,&k);
long long tmp=n;
while(tmp) t++,tmp/=2;
fac[0]=1;for(int i=1;i<=t;i++) fac[i]=1ll*fac[i-1]*(k+i-1)%mod;
inv[0]=inv[1]=1;for(int i=2;i<=t;i++) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=t;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
long long ans=0,l=1,r;
while(l<=n){
r=n/(n/l);
ans=(ans+sum(l,r)*sum(1,n/l))%mod;
l=r+1;
}
g[0][0]=1;
for(int i=1;i<=t;i++){
for(int j=0;j<=i;j++){
f[i][j]=f[i-1][j]*2%mod;g[i][j]=g[i-1][j];
if(j) f[i][j]=(1ll*f[i][j]+f[i-1][j-1]*2+g[i-1][j-1])%mod,g[i][j]=(g[i][j]+g[i-1][j-1])%mod;
}
}
for(int i=1;i<t;i++){
long long tmp=(n-(1ll<<i))/(1ll<<i+1),I=(1ll<<i)%mod;
for(int j=0;j<i;j++){
long long J=(1ll<<j)%mod,JJ=(1ll<<j+1)%mod;
int q=i-j-1;
for(int w=0;w<=q;w++){
long long l=0,r,tot=0;
while(l<=tmp){
r=(2*tmp+1)/((2*tmp+1)/(2*l+1));
if(!(r&1)) r--;r>>=1;
long long L=l%mod,R=r%mod,T=((2*tmp+1)/(2*l+1)-1)/2%mod,S=(T+1)*(R+mod-L+1)%mod;
tot=(tot+((T+1)*(T+1)%mod*((L+R)*(R+mod-L+1)%mod+R+mod-L+1)%mod+mod-S)*I%mod*g[q][w])%mod;
tot=(tot+(1ll*f[q][w]*JJ+1ll*g[q][w]*J)%mod*S)%mod;
l=r+1;
}
ans=(ans+tot*(i+1)%mod*fac[q-w+1]%mod*inv[q-w+1])%mod;
}
}
}
printf("%lld\n",ans);
}
树状数组与约数个数计算
本文介绍了一段代码,该代码使用树状数组实现数组的变换,并计算约数个数之和。作者定义了一个CCC变换,用于将数组转换为其树状数组形式。然后,通过对初始数组进行多次CCC变换,计算每个元素的约数个数并累加。最后,文章提供了数学分析和解决方案,涉及到组合计数和分块除法。代码涉及数论和数据结构的结合,适用于解决复杂计算问题。
926

被折叠的 条评论
为什么被折叠?



