hdu 6275 Mod, Xor and Everything (类欧几里得)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6275

题意:求 (n mod 1)⊕(n mod 2)⊕.......⊕(n mod n)
数据范围:n≤1e12

 

思路:首先对于取模操作是可以转化成公式的,nmodi=n-\left \lfloor \frac{n}{i} \right \rfloor*i,此题因为是异或操作我们可以一位一位的算贡献,对于第k位的贡献可以通过将n个数右移k位以后只看最后一位的异或,又因为只看最后一位的异或其实就是对右移完的n个数求和mod2,求和以后mod2和只看最后一位异或是等价的,那么对于第k为的贡献是((\sum_{1}^{n}\left \lfloor \frac{n-\left \lfloor \frac{n}{i} \right \rfloor*i}{ 2^k} \right \rfloor mod2)mod2)*2^k,现在我们发现我们有了一个\sum_{i=0}^{n}\left \lfloor \frac{ai+b}{c} \right \rfloor的形式,那么就想到了类欧几里得算法,它可以O(logn)的解决这个问题。但是此题中的\left \lfloor \frac{n}{i} \right \rfloor是在变化的,那么我们就想到了整式分块,下面的j代表n/(n/i),也就是分的块的大小,对于1到3e7直接暴力求然后异或,对与大于3e7的分块,1e12/3e7<1e5那么最多会分成1e5块,对于每一块跑一个类欧几里得就好了,时间复杂度是O(3e7+1e5*logn)。但是类欧几里得算法的系数a和b都不能是负数只能接着化简,对于每次分好块以后只需要求一下\sum_{u=i}^{j} \frac{\left \lfloor n-\left \lfloor \frac{n}{i} \right \rfloor *u \right \rfloor}{2^k},他可以化简成\sum_{u=i-j}^{0} \frac{\left \lfloor n-\left \lfloor \frac{n}{i} \right \rfloor *(u+j) \right \rfloor}{2^k}将u变成-u,就可以化简成\sum_{u=0}^{j-i} \frac{\left \lfloor n-\left \lfloor \frac{n}{i} \right \rfloor *(-u+j) \right \rfloor}{2^k}再化简一下就是\sum_{u=0}^{j-i} \frac{\left \lfloor n-\left \lfloor \frac{n}{i} \right \rfloor *j+\left \lfloor \frac{n}{i} \right \rfloor *u \right \rfloor}{2^k},因为是整数分块所以\left \lfloor \frac{n}{i} \right \rfloor=\left \lfloor \frac{n}{j} \right \rfloor,所以就可以化简成\sum_{u=0}^{j-i} \frac{\left \lfloor n-\left \lfloor \frac{n}{j} \right \rfloor *j+\left \lfloor \frac{n}{i} \right \rfloor *u \right \rfloor}{2^k}我们观察到n-\left \lfloor \frac{n}{j} \right \rfloor *j,就可以化简为\sum_{u=0}^{j-i} \frac{\left \lfloor nmodj+\left \lfloor \frac{n}{i} \right \rfloor *u \right \rfloor}{2^k}那么系数a=\left \lfloor \frac{n}{i} \right \rfloor,系数b=n%j,带入类欧几里得就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5;
int t;
int f(ll a,ll b,ll c,ll n){
    ///因为防止爆long long 所以mod2,&1比mod2快很多
    if(!a)return (((n+1)&1)*(b/c))&1;
    if(a>=c||b>=c){
        ll res=((n%4)==0||(n+1)%4==0)?0:1;
        return ((f(a%c,b%c,c,n)&1)+(((a/c)*res)&1)+(((n+1)&1)*(b/c)&1))&1;
    }
    else{
        ll m=(a*n+b)/c;
        return (((m*n)&1)^f(c,c-b-1,a,m-1))&1;
        ///减法可以变异或因为减法也是奇偶相同为偶,不同为奇
    }
}
int main(){
    scanf("%d",&t);
    while(t--){
        ll n;
        scanf("%lld",&n);
        ll ans=0,sqrtn=min(30000000ll,n);
        for(ll i=1;i<=sqrtn;i++)ans^=n%i;
        for(ll i=sqrtn+1,j;i<=n;i=j+1){
            j=n/(n/i);
            ll lim=n/i*(j-i)+n%j,res=0;
            for(ll k=1; k<=lim; k<<=1)
                res+=f(n/i,n%j,k,j-i)*k;
            ans^=res;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值