JZOJ5894【NOIP2018模拟10.5】同余方程
题目
Description
Input
Output
Sample Input
123 1234 234 2345 5
Sample Output
470244
Data Constraint
题目大意
求满足的个数,其中
思考历程
一眼望去,毫无头绪……
异或会和取模有什么关系?
想半天,拿了30分暴力,便弃之。
正解
没错,异或和取模的确没有什么关系。
所以不要认为是什么奇葩数论题。
正确的思路是将其分成许多段连续的区间,然后直接计算贡献。
那么怎么划分?
首先,我们可以将先当成。因为只要求出这个情况的答案,那么就可以通过二维前缀和的方式算出整一题的答案。
对于上界和,分别枚举它们为的位数(作为第一个小于上界的位)。
设这两个位数分别为和。
如果比高的位不变,将设为,那么比低的位无论如何取值,都不可能超过上限。
设
我们可以将两者异或起来的结果分为三段。
首先是最高位到位,这些位的异或结果是固定的。
然后到位,在这里的异或结果可以是任意的,并且每一个异或结果对应着唯一的一种方案。
最后是到最低位,这里的异或结果也是任意的,并且每一个异或结果对应着种方案。
异或的结果的取值范围为,t为前面已经确定的异或结果。
这样就可以直接通过除法算出这段区间的贡献了。
即
还有,求的时候有一点需要注意:
已经确定的部分是及之前的部分,而对于为要特殊处理一下。
如果,那么位中两者都是,异或起来为,相当于,不理它。
如果,由于位需要修改成,所以需要将为取个反。
详见一下代码。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define mod 998244353
long long l1,r1,l2,r2,m;
long long ans(long long,long long);
int main(){
freopen("mod.in","r",stdin);
freopen("mod.out","w",stdout);
scanf("%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&m);
printf("%lld\n",(ans(r1,r2)-ans(l1-1,r2)-ans(r1,l2-1)+ans(l1-1,l2-1)+mod+mod)%mod);
return 0;
}
long long ans(long long x,long long y){
x++,y++;
long long res=0;
for (int i=59;i>=0;--i)
if (x>>i&1)
for (int j=59;j>=0;--j)
if (y>>j&1){
int mx=max(i,j),mn=i+j-mx;
long long cur=(x^y^((i==j)?0:1ll<<mx))>>mx<<mx;//具体解释见上,还有后面的右移左移是为了将后面的东西清除掉
if (cur==0)
res=(res+(((1ll<<mx)-1)/m+1)%mod*((1ll<<mn)%mod)%mod)%mod;//当cur=0时,(cur-1)为负数,所以(cur-1)/m会上取整……所以直接特判
else
res=(res+(((cur+(1ll<<mx)-1)/m-(cur-1)/m)%mod+mod)%mod*((1ll<<mn)%mod)%mod)%mod;
}
return res;
}