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
*/