1. 程式人生 > >[JZOJ5894] 【NOIP2018模擬10.5】同餘方程

[JZOJ5894] 【NOIP2018模擬10.5】同餘方程

Description

給出 l 1 , r 1 , l

2 , r 2 , m l_1,r_1,l_2,r_2,m ,求有多少組(x,y),滿足 x
[ l 1 , r 1 ] ,
y [ l 2 , r 2 ] , x   x o r   y 0 ( m o d m ) x\in [l_1,r_1],y\in [l_2,r_2],x\ xor\ y \equiv0\pmod m

l 1 , r 1 , l 2 , r 2 1 0 18 , m 1 0 9 l_1,r_1,l_2,r_2\leq 10^{18},m\leq 10^9
其中一檔部分分 l 1 = r 1 l_1=r_1

Solution

先從部分分入手,考慮l1=r1怎麼做
首先肯定是拆成 [ 1 , l 1 1 ] [1,l_1-1] [ 1 , r 1 ] [1,r_1] 兩部分來做(二維類似二維字首和一樣拆)

我們可以列舉一個i,表示y的前i-1位是和上界相同,第i位小於上界,那後面就可以隨便選了,x的後面位置是沒有用的,因為無論我們需要什麼,我們都可以通過改變y來使得它異或成我們需要的,並且是唯一對應的。

現在問題變成y從i+1位開始可以隨便選,k可以隨便選,要滿足 x   x o r   y = k m , k N x\ xor\ y =km,k\in \N
既然y從i+1位可以隨便選,也就是說k*m要滿足前i位等於x xor y的前i位
這樣的k顯然是一個區間,直接上界下界除一下再減就算出來了

現在迴歸原題,x也是可變的
其實思路是一樣的,我們再列舉x的前j位與上界相同,第j位小於上界
考慮x xor y,前min(i,j)位是兩個都確定,min(i,j)+1到max(i,j)位是確定一個,這部分和前面是一樣的,後面的位兩個都不確定,相當於無論x後面怎麼選,都能通過改變y來達到,因此只需要將第一部分的答案乘上2^(後面的位數)即可。

注意我們這樣算出來其實上界是r-1的(因為我們每一位都保證小於上界)
因此實際上做的時候要把上界+1
複雜度 O ( log 2 r ) O(\log^2 r)

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
#define mo 998244353
using namespace std;
LL l1,r1,l2,r2,m,cf[61];
LL get(LL m1,LL m2)
{
	LL s=0;
	fod(i,59,0)
	{
		if(m1&cf[i])
		fod(j,59,0)
		{
			if(m2&cf[j])
			{
				int c=max(i,j);
				LL v=((m1^m2^((i==j)?0:cf[c]))|(cf[c]-1))^(cf[c]-1);
				if(v==0) s=(s+((cf[c]-1)/m+1)%mo*(cf[min(i,j)]%mo)%mo)%mo;
				else s=(s+(((v+cf[c]-1)/m-(v-1)/m)%mo+mo)%mo*(cf[min(i,j)]%mo)%mo)%mo;
			}
		}
	}

	return s;
}
int main()
{
	cin>>l1>>r1>>l2>>r2>>m;
	LL s=0;
	cf[0]=1;
	fo(i,1,60) cf[i]=cf[i-1]*(LL)2;
	printf("%lld\n",(get(r1+1,r2+1)-get(l1,r2+1)-get(r1+1,l2)+get(l1,l2)+mo+mo)%mo);
}