牛客网比赛211F题算式子题解
题目描述
题目地址【IN】
给你个整数,保证。
对于每一个,我们令
然后请求出
(异或和,也就是)
- 数据范围:
首先暴力就是的了,就不说了。
- 正解
那么我们发现,对于原来的每个的式子,我们可以拆成两部分来看:
对于第一个式子,我们可以通过枚举,来算贡献,我们发现对于每一个,只有当跨过它的倍数时才会变,又因为值范围较小,所以我们令表示序列中权值在的个数和,那么枚举倍数,每一个的贡献为,所以第一个式子通过枚举及其倍数求出,复杂度为调和级数
对于后面一个式子,我们反着枚举,枚举的倍数,所以先离散化,记录每个出现多少次,然后我们同样对于枚举的一个,它的贡献为(表示的个数,这里必须离散化,否则多个可以将复杂度卡到)
但是上面这个是有问题的,有的非的整倍数的贡献没有统计上,所以我们换个思路。
我们发现对于一个较小的,如果有贡献,那么对于大的肯定也有贡献,所以我们计算的时候,只算当前的贡献,然后每个的总贡献通过前缀和即可算出,复杂度仍旧为。
所以总复杂度为
丑陋QWQ代码,记得开
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=4e6+10;
ll n,m,A[M],B[M];
ll pref1[M],pref2[M],ans,cnt[M];
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)scanf("%lld",&A[i]),++B[A[i]],++cnt[A[i]];
for(ll i=1;i<=m+m;i++)B[i]+=B[i-1];
for(ll i=1;i<=m;i++){
for(ll j=i,tc=1;j<=m;j+=i,++tc)
pref1[i]+=(B[j+i-1]-B[j-1])*tc;
}
sort(A+1,A+n+1);
int up=unique(A+1,A+n+1)-A-1;
for(ll i=1;i<=up;i++){
for(ll j=A[i];j<=m;j+=A[i])pref2[j]+=cnt[A[i]];
}//注意:这里不用乘以它的贡献(cnt[A[i]]*(j/A[i])是因为小的肯定会对大的做出贡献,所以我们再求一次前缀和就好了。
//相当于加上了贡献,因为对于j小的pref2[j],它的贡献是会累计到后面的
for(ll i=1;i<=m;i++)pref2[i]+=pref2[i-1];
for(ll i=1;i<=m;i++){
ans^=(pref1[i]+pref2[i]);
}
printf("%lld\n",ans);
return 0;
}