题目
T(T<=5)组样例,每次给定一个正整数n(n<=1e12)
求(n mod 1) xor (n mod 2) xor ... xor (n mod (n - 1)) xor (n mod n)
思路来源
https://blog.youkuaiyun.com/Code92007/article/details/97396823
https://blog.youkuaiyun.com/qq_43316231/article/details/99751317(相当清晰的题解QAQ)
题解
类似【GDOI2018模拟8.8】超级绵羊异或
求(a) xor (a + b) xor (a + b * 2) xor … xor (a + b * (n - 1))。
对于100%的数据,t<=1e4,a, n<=1e9, b<=1e9;
写成求和形式,考虑最后第k位的奇偶,判断每一位的出现次数,再类欧
考虑到本题中,把相同的系数放在一起处理,数论分块搞一下就好了
考虑第k位的贡献,,
但是,类欧只能处理系数和初值都为非负的情况,所以对于n/i相同的一段[i,j],经历如下化简
原式子,如果把系数看成非负的(n/i),现在的变量其实是x=-u∈[-j,-i],
所以要作变换,把u通过平移和取反,搞成是[0,j-i](j-i>0)的一段
先提一个j出来,
再令u=-u,,
化简成这个形式,发现a和b都非负了,也都能求,
令a=n/i,b=n-[n/i]*j,c=1<<k,n=j-i,就可以直接丢进去做了
为了优雅一点,由于n/i相同的一段[i,j],有,
有,注意到n-[n/j]*j=n%j
令a=n/i,b=n%j,c=1<<k,n=j-i,再丢进去
然后,这个题它卡常,所以小范围暴力,大范围类欧,有两种方法
①前3e7数据暴力,3e7到1e12再类欧,效果不是很明显
②对于数论分块的区间段[l,r],如果长度超过1e4再类欧,否则直接暴力,效果显著
由于f函数,只需要最后的结果%2,所以所有的中间结果都%2了,
变成了bool函数,位运算&1和^1写更快
代码
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll unsigned long long
using namespace std;
//sigma i=1到n [(ai+b)/c]下取整
bool f(ll a,ll b,ll c,ll n){
if(a==0){
return (((n+1)&1)*(b/c))&1;//%mod;
}
if(a<c && b<c){
ll m=(a*n+b)/c;
if(m==0)return 0;
return ((n*m)^f(c,c-b-1,a,m-1))&1;//+mod)%mod;
}
return ((b/c)*(n+1)+n*(n+1)/2*(a/c)+f(a%c,b%c,c,n))&1;//%mod;
}
ll n,l,r,mn,a,b,ans;
ll solve(ll a,ll b,ll n){
ll ret=0;
if(n<=10000){//暴力
for(int i=0;i<=n;++i){
ret^=b;b+=a;
}
}
else{//类欧
for(int i=0;i<40;++i){
ret^=((ll)f(a,b,1ll<<i,n))<<i;
}
}
return ret;
}
int main(){
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++){
ans=0;
scanf("%llu",&n);
for(l=1;l<=n;l=r+1){
r=n/(n/l);//[l,r]这一段
ans^=solve(n/l,n%r,r-l);
}
printf("%llu\n",ans);
}
return 0;
}