BZOJ 4513 储能表

数位DP?我哪会

[0,n-1]可以拆成log段一定前缀之后任意的区间

\(log^2\) 枚举区间对计数即可

#include <cstdio>
#include <algorithm>

using std::min;
using std::max;

int T;

long long N, M, K;
long long n, m, kl, kr;

int Cnt, Sum;

int MOD;

int sr;
int sum(int a, int b){
	sr=a+b;
	if(sr>=MOD)	sr-=MOD;
	return sr;
}

int mul(int a, int b){
	return (int)((1LL*a*b)%(long long)(MOD));
}

int Norm(long long n){
	return (int)(n%(long long)(MOD));
}

int Calc(long long n){
	if(n&1LL)
		return mul(Norm(n), Norm((n+1LL)>>1));
	else	return mul(Norm(n>>1), Norm(n+1LL));
}

void Update(long long l, long long r, long long k){
	Cnt=sum(Cnt, mul(Norm(r-l), Norm(k)));
	Sum=sum(Sum, mul(sum(Calc(r), MOD-Calc(l)), Norm(k)));
}

int main(){
	
	scanf("%d", &T);
	
	while(T--){
		
		scanf("%lld%lld%lld%d", &N, &M, &K, &MOD);
		
		Sum=0;Cnt=0;
		
		for(int i=0;i<60;++i){
			if((N>>i)&1){
				n=(N>>(i+1))<<(i+1);
				for(int j=0, l;j<60;++j){
					if((M>>j)&1){
						m=(M>>(j+1))<<(j+1);
						l=max(i, j);
						kl=n^m;kl=(kl>>l)<<l;
						kr=kl+(1LL<<l)-1LL;
						if(K<kr)	Update(max(kl-1, K), kr, (1LL<<min(i, j)));
					}
				}
			}
		}
		
		Sum=sum(Sum, MOD-mul(Cnt, Norm(K)));
		
		printf("%d\n", Sum);
		
	}
	
	
	
	
	return 0;
}

/*
3
2 2 0 100
3 3 0 100
3 3 1 100

2
12
6

*/

posted @ 2019-03-09 13:57  Pickupwin  阅读(186)  评论(0编辑  收藏  举报