思路
令 为 , 为 ,求前缀和。 一定是前缀是 ,后缀是 ,并且 前面有 个 。因为 的 数量与 相同,那么只要两者的前缀和的 min 一样,就有 。也就是数 的 的数量。
E1
设 表示从 到 且不碰 的路径数,其中要求 。则由反射容斥,。只有 个组合数有值。
枚举 ,要求从 到 , 向右, 向上,恰好碰到 和 ,即 。
对 的 一起计算,枚举 。当 从 移动到 时, 也从 移动到 ,带进 求出边界后,第一个 枚举的相当于第 行组合数的区间和,第 个 枚举的相当于 个 。另外三项大致相同,特别的,因为 对称,减去的两组 的值应当是相等的。
复杂度 。
code
int calc(int n,int m,int l,int r){
// cout<<n<<" "<<m<<" "<<l<<" "<<r<<endl;
// if(l>=0||r<=0)return 0;
// if(n+l>=m||n+r<=m)return 0;
// if(l==r)return 0;
int res=0;
for(int k=-(m+r-l-1)/(r-l);k<=n/(r-l);k++)(res+=C(n+m,n-k*(r-l)))%=mod;
for(int k=-(m-r+r-l-1)/(r-l);k<=(n+r)/(r-l);k++)(res+=mod-C(n+m,n-k*(r-l)+r))%=mod;
return res%mod;
}
int sum[maxn];
void work(){
n=read();ans=0;
for(int i=0;i<=n;i++)sum[i]=C(n,i);
for(int i=1;i<=n;i++)(sum[i]+=sum[i-1])%=mod;
for(int v=2-(n&1);v<=n;v+=2){
//f(n,m,l,r)
for(int k=-((n+v)/2+v-1)/v;k<=(n+v)/2/v;k++){
(ans+=que((n-v)/2-k*v,(n+v)/2-k*v))%=mod;
}
for(int k=-((n-v)/2+v-1)/v;k<=(n+v)/2/v;k++){
(ans+=mod-C(n,(n+v)/2-k*v)*(v+1)%mod)%=mod;
}
//f(n,m,l-1,r+1)
for(int k=-((n+v)/2+(v+2)-1)/(v+2);k<=(n+v)/2/(v+2);k++){
(ans+=que((n-v)/2-k*(v+2),(n+v)/2-k*(v+2)))%=mod;
}
for(int k=-((n-v)/2-1+(v+2)-1)/(v+2);k<=((n+v)/2+1)/(v+2);k++){
(ans+=mod-C(n,(n+v)/2+1-k*(v+2))*(v+1)%mod)%=mod;
}
//f(n,m,l,r+1)
for(int k=-((n+v)/2+(v+1)-1)/(v+1);k<=(n+v)/2/(v+1);k++){
(ans+=2*mod-2*que((n-v)/2-k*(v+1),(n+v)/2-k*(v+1)))%=mod;
}
for(int k=-((n-v)/2-1+(v+1)-1)/(v+1);k<=((n+v)/2+1)/(v+1);k++){
(ans+=2*C(n,(n+v)/2+1-k*(v+1))*(v+1)%mod)%=mod;
}
// for(int l=-v;l<=0;l++){
// int r=l+v;
// (ans+=mod-calc((n+l+r)/2,(n-l-r)/2,-r-1,-l)+mod-calc((n+l+r)/2,(n-l-r)/2,-r,-l+1))%=mod;
// }
}
printf("%lld\n",ans);
}E2
从 E1 到 E2 根本不是人。
发现组合数求区间和的部分,实际上是 和 个等差数列上的单点。
相互抵消之后,可以写成 行:
for(int i=0;i<=n;i++)val[i]=C(n,i);
for(int v=2-(n&1);v<=n;v+=2){
for(int k=(n+v)/2%v;k<=n;k+=v)ans-=v*val[k]%mod;
for(int k=((n+v)/2+1)%(v+2);k<=n;k+=v+2)ans+=mod-(v+2)*val[k]%mod;
for(int k=((n+v)/2+1)%(v+1);k<=n;k+=v+1)ans+=2*(v+1)*val[k]%mod;
}很有规律的样子,把系数打表出来。设 求 的答案时 前乘的系数。
3 -1
4 0 -2
1 3 -1 -3
2 4 0 -2 -2
5 1 3 -1 -3 -3
0 2 4 0 -2 -2 -4
1 5 1 3 -1 -3 -3 1
8 0 2 4 0 -2 -2 -4 -2
-1 1 5 1 3 -1 -3 -3 1 -9
0 8 0 2 4 0 -2 -2 -4 -2 2 发现 。
再尝试把 种 的系数分别打出来。
第一种:
-1,-1,
-2,0,-2,
-4,-1,-1,-4,
-4,-2,0,-2,-4,
-6,-4,-1,-1,-4,-6,
-8,-4,-2,0,-2,-4,-8,
-8,-6,-4,-1,-1,-4,-6,-8,
-8,-8,-4,-2,0,-2,-4,-8,-8,
-13,-8,-6,-4,-1,-1,-4,-6,-8,-13,
-12,-8,-8,-4,-2,0,-2,-4,-8,-8,-12,对于第一种, 依旧成立。还有 。那 就等于 。
OEIS ,是 A002131,即 的奇因数 的 之和 。大概也能证明,,即 的 有 的贡献。
可以猜测第 和 种大概就是这个基础上有偏移和加减。
第二种除以 :
2,0,
3,0,0,
4,2,0,2,
5,3,0,0,3,
8,4,2,0,2,4,
7,5,3,0,0,3,5,
8,8,4,2,0,2,4,8,
12,7,5,3,0,0,3,5,7,
12,8,8,4,2,0,2,4,8,8,
11,12,7,5,3,0,0,3,5,7,12,很难发现:在奇数行,就是 A002131 的偶数项;在偶数行,是 A002131 的奇数项减 。
第三种:
0,0,
0,0,0,
3,0,0,3,
4,0,0,0,4,
5,3,0,0,3,5,
6,4,0,0,0,4,6,
7,5,3,0,0,3,5,7,
8,6,4,0,0,0,4,6,8,
12,7,5,3,0,0,3,5,7,12,
10,8,6,4,0,0,0,4,6,8,10,
11,12,7,5,3,0,0,3,5,7,12,11,
16,10,8,6,4,0,0,0,4,6,8,10,16,很难发现:在奇数行,是 A002131 的奇数项减 ;在偶数行,是两倍的 A352047,而 A352047,即 的真奇因数 的 之和,是 A002131 在奇数时减一。
至于求 A002131,即 ,线性筛即可。注意要筛到 。
复杂度 。
code
int val[maxn];
int f[maxn];
int pre[maxn],cnt;
bool vis[maxn];
int si[maxn];
void init(int n){
si[1]=1;for(int i=2;i<=n;i++){
if(!vis[i])pre[++cnt]=i,si[i]=(i==2?2:i+1);
for(int j=1;j<=cnt&&i*pre[j]<=n;j++){
vis[i*pre[j]]=1;
if(i%pre[j]==0){
if(pre[j]==2)si[i*pre[j]]=pre[j]*si[i];
else si[i*pre[j]]=si[i]+pre[j]*(si[i]-si[i/pre[j]]);
break;
}
if(pre[j]==2)si[i*pre[j]]=pre[j]*si[i];
else si[i*pre[j]]=(1+pre[j])*si[i];
}
}
}
void work(){
n=read();ans=0;
for(int i=0;i<=n;i++)val[i]=C(n,i);
for(int i=0;i<=n;i++)f[i]=0;
// for(int v=2-(n&1);v<=n;v+=2){
// for(int k=(n+v)/2%v;k<=n;k+=v)f[k]-=v;
// for(int k=((n+v)/2+1)%(v+1);k<=n;k+=v+1)f[k]+=2*(v+1);
// for(int k=((n+v)/2+1)%(v+2);k<=n;k+=v+2)f[k]-=v+2;
// }
for(int i=0;i<=n;i++)f[i]+=mod-si[abs(n-2*i)];
if(n&1){
for(int i=0;i<=n;i++)f[i]+=2*si[abs(n-2*i-1)];
}
else{
for(int i=0;i<=n;i++)f[i]+=2*(si[abs(n-2*i-1)]-1);
}
if(n&1){
for(int i=0;i<=n;i++)f[i]+=mod-(si[abs(n-2*i)]-1);
}
else{
for(int i=0;i<n/2;i++)f[i]+=mod-2*(si[n/2-i]-((n/2-i)&1));
for(int i=n/2+1;i<=n;i++)f[i]+=mod-2*(si[i-n/2]-((i-n/2)&1));
}
// for(int i=0;i<=n;i++)cout<<f[i]<<" ";cout<<"\n";
for(int i=0;i<=n;i++)(ans+=f[i]*val[i])%=mod;
ans%=mod,ans+=mod,ans%=mod;
printf("%lld\n",ans);
}