版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88781512
title
定义f(i)为i的所有约数的异或和,给定 n(1≤n≤1014) ,求f(1)xorf(2)xorf(3)xor…xorf(n) (其中xor表示按位异或)
analysis
首先考虑到枚举因数x,然后算出他是小于等于n的数字中x的倍数的个数,即 ,然后根据奇偶性判断是否要异或x。
这样复杂度是 的,看到 很容易想到数论分块。
然后问题就是如何快速查询连续区间的异或和。
设
那么区间
的异或和就是
然后对于s数组打个表如下
可以发现在模4意义下是有规律的。然后就可以O(1)计算连续区间异或和了。
上面的都是来自wxyww,觉得他讲的很好。
反正我在比赛时,是想到了O(n)算法的,然后一个劲儿的想优化,问了邱神,邱神透露了一点“你会求连续自然数的异或和吗?”,然后康神就不让他说了。我想:这肯定是在暗示这道题和连续自然数异或和之间有规律,然后打表找规律,然后自己想出来的两个所谓优化,都被毙了。TAT
哎,不说伤心事了,放代码啦!
code
O(N)算法。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int main()
{
ll n;read(n);
int ans=0;
for (int i=1;i<=n;++i)
if (n/i&1)
ans^=i;
printf("%d\n",ans);
return 0;
}
AC代码。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
inline ll getans(ll x)
{
if (x%4==1) x=1;
else if (x%4==2) ++x;
else if (x%4==3) x=0;
return x;
}
inline ll ask(ll l,ll r)
{
return getans(r)^getans(l-1);
}
int main()
{
ll n;read(n);
ll ans=0;
for (ll l=1,r; l<=n; l=r+1)
{
r=n/(n/l);
if (n/l&1)
ans^=ask(l,r);
}
printf("%lld\n",ans);
return 0;
}
appendix
另外,怎样打表才能更容易发现规律呢?找邱神啊!
#include<bits/stdc++.h>
using namespace std;
inline void work(int n)
{
for (int i=1; i<=n; ++i)
{
if (n/i&1)
cout<<i<<" ";
}
}
int main()
{
for (int i=1; i<=30; ++i)
work(i);
return 0;
}
结果如下: