【BZOJ4513】【Sdoi2016】储能表

听说标解是个神数位DP什么的。。。讲道理我是辣鸡我不会。

所以说异或运算真的是个神奇的东西,打一张表出来仿佛各处都藏着奇葩的规律,因为有k的限制,所以我们的目的是将整个序列化成若干个等差序列的和,并且每个等差序列从k处断开(因为负数不计入计算),不难发现存在很多个长边为(2^i),短边小于等于长边的矩形,其中每一条长边都是一个打散的等差数列,我们每次从子矩形中拿出最大的一个,计算一个再乘以一共有几条,剩下的部分递归分治解决即可。

#include<cstdlib> 
#include<cstdio> 
#include<iostream> 
#include<cstring> 
#include<cmath> 
#include<algorithm> 
#include<queue> 
#include<vector> 
using namespace std; 
#define maxn 1005

void _readLL(long long &x) 
{ 
    x=0; char ch=getchar(); bool flag=false; 
    while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} 
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}	if(flag)x=-x; 
    return ; 
} 
long long n,m,k,p;
long long ans,sum;
void calc(long long ct,long long A0,long long An)
{
	sum=0;
	if(An<=k)return ;
	else if(A0<=k) 
	{
		if((An-k)%2==0) sum=((An-k)/2)%p*((An-k+1)%p)%p;
		else sum=((An-k+1)/2)%p*((An-k)%p)%p;
	}
	else 
	{
		sum=(A0-k+An-k)*(An-A0+1)/2;
		if((An+A0-2*k)%2==0) sum=((A0-k+An-k)/2)%p*((An-A0+1)%p)%p;
		else sum=((An-A0+1)/2)%p*((A0-k+An-k)%p)%p;
	}
	sum=((sum%p)*(ct%p))%p; ans=(ans+sum)%p;
	return ;
}
long  long Findmin(long long L,long long R,long long x)
{
	long long e=1; int ni;	for(ni=62;ni>=0;ni--)if((R-L+1)>=(e<<ni))break;
	return ni>0 ? ((x>>(ni-1))<<(ni-1))^L : x^L;
	
}
void run(long long X1,long long Y1,long long X2,long long Y2)
{
	if(X1>X2 || Y1>Y2 ) return ;
	int ni,nj;long long e=1,t;
	for(ni=62;ni>=0;ni--)if((X2-X1+1)>=(e<<ni))break;
	for(nj=62;nj>=0;nj--)if((Y2-Y1+1)>=(e<<nj))break;
	//t=Findmin(X1,X1+(e<<ni)-1,Y1,Y1+(e<<nj)-1);
	if(ni<nj){	swap(ni,nj); swap(X1,Y1); swap(X2,Y2);	}
	if(Y2-Y1+1<=(e<<ni))
	{
		t=Findmin(X1,X1+(e<<ni)-1,Y1);
		calc(Y2-Y1+1,t,t+(e<<ni)-1);
		run(X1+(e<<ni),Y1,X2,Y2);
	}
	else
	{
		t=Findmin(X1,X1+(e<<ni)-1,Y1);
		calc((e<<ni),t,t+(e<<ni)-1);
		t=Findmin(X1,X1+(e<<ni)-1,Y1+(e<<ni));
		calc(Y2-Y1+1-(e<<ni),t,t+(e<<ni)-1);
		run(X1+(e<<ni),Y1,X2,Y2);
	//	run(X1,Y1+(e<<ni),X1+(e<<ni)-1,Y2);
	}
	return ;
}
void Debug()
{
	long long ans=0;
	for(int i=0;i<=n;i++)
	{
		for(int j=0;j<=m;j++)
		{
			printf("%d ",i^j);
			if((i^j)>=k)ans=(ans+(i^j)-k)%p;
		}
		putchar('\n');
	}
	cout<<ans<<endl;
	return ;
}
char s[35];int cc;
void out(long long x)
{
	if(!x)putchar('0');
	if(x<0){x=-x; putchar('-');}
	cc=0; while(x){s[++cc]=x%10+'0';x=x/10;}
	while(cc){putchar(s[cc]);cc--;}
	putchar('\n');
	return ;
}
int main()
{
	freopen("in.txt","r",stdin);
	
	int T;scanf("%d",&T);
	
	while(T--)
	{
		ans=0;
		_readLL(n);_readLL(m); _readLL(k); _readLL(p);n--; m--;
	//	Debug();
		run(0,0,n,m);
		out(ans);
		
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值