专栏文章
CF1359E
CF1359E题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minhvtjn
- 此快照首次捕获于
- 2025/12/02 02:40 3 个月前
- 此快照最后确认于
- 2025/12/02 02:40 3 个月前
题面
有一个长度为 的序列,不管他怎么排列变换,对于任意的自然数 ,满足 为定值。
求序列所有元素不大于 且序列元素互不相同的方案数,一个序列通过排列变换得到的序列不算一个新的序列。
题解
题目里给的是 ,我换成了序列元素互不相同,因为一个显然的结论:当序列为如上的形式时,, 只由 决定。而要证明这个序列的合法性,肯定要倒过来,,这时候 就会受到数组中其他数的影响了。
然后把上面两种情况取一下,要有当序列倒过来时 。
猜结论环节。先猜一下所有数都是 的倍数,然后把 和 代入发现还真行(考试时到这一步就可以打代码了)。
但是这里是题解,所以我要证明(呜呜呜)。设 ,,然后有一个正整数 ,令 ,,则要证明 。
,则 ,同理 ,,则 ,所以 。
证完了看怎么构造数组。应该构造为固定第一个数,后面均为其倍数且互不相同。设这个数为 ,则在 范围内,有 个 的倍数(除了 ),除开第一个位置以外,有 个位置,然后排列变换后的不算多出来的方案。所以对于一个数 ,贡献值为 ,然后枚举 即可。注意 (你甚至可以用整除分块,但我测下来发现运行时间没多大差别)。
代码
CPP#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll p=998244353;
const int N=5e5+5;
ll n,k,fac[N],inv[N],ans;
ll power(ll a,ll b) {
ll res=1;
while(b) {
if(b&1) res=res*a%p;
a=a*a%p,b>>=1;
}
return res;
}
ll C(ll x,ll y) {
return fac[x]*inv[y]%p*inv[x-y]%p;
}
int main() {
scanf("%lld%lld",&n,&k);
fac[0]=inv[0]=1ll;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%p,inv[i]=power(fac[i],p-2);
for(int l=1,r;l<=n/k;l=r+1) {
r=min(n/(n/l),n/k);
ans+=(r-l+1)*C(n/l-1,k-1);
}
/*
上面是整除分块。这个等效于下面的代码:
for(int i=1;i<=n/k;i++) ans+=C(n/i-1,k-1);
*/
printf("%lld",ans%p);
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...