bzoj4513 储能表 数位dp

        令f[len][S1][S2][S3]表示前len位,i满足状态S1,j满足状态S2,i^j满足状态S3。S1=0表示i的前len位比n的前len位小,1表示不作要求;S2同理;S3=0表示i^j>=k,否则表示不作要求。

       然后就可以轻松转移辣~\(≧▽≦)/~。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define ll long long
#define N 105
using namespace std;

ll n,m,k; int p,lenm,lenn,lenk,len,a[N],b[N],c[N],f[N][2][2][2],g[N][2][2][2],bin[N];
void ad(int &x,int y){ x+=y; if (x>=p) x-=p; }
int main(){
	int cas,i,x,y,z; scanf("%d",&cas);
	while (cas--){
		scanf("%lld%lld%lld%d",&m,&n,&k,&p);
		memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c));
		lenm=0; for (; m; m>>=1) a[++lenm]=m&1;
		lenn=0; for (; n; n>>=1) b[++lenn]=n&1;
		lenk=0; for (; k; k>>=1) c[++lenk]=k&1;
		len=max(max(lenm,lenn),lenk);
		bin[0]=1; for (i=1; i<=len; i++) bin[i]=(bin[i-1]<<1)%p;
		int i,j,k,l,x,y,z,u,v,tmp;
		memset(f,0,sizeof(f)); memset(g,0,sizeof(g));
		for (x=0; x<2; x++) for (y=0; y<2; y++) for (z=0; z<2; z++)
			for (u=0; u<((x)?2:a[1]); u++)
				for (v=0; v<((y)?2:b[1]); v++)
					if ((u^v)>=c[1] || z){
						ad(f[1][x][y][z],((u^v)-c[1]+p)%p);
						ad(g[1][x][y][z],1);
					}
		for (i=2; i<=len; i++)
			for (x=0; x<2; x++) for (y=0; y<2; y++) for (z=0; z<2; z++)
				for (u=0; u<=max(x,a[i]); u++)
					for (v=0; v<=max(y,b[i]); v++)
						if ((u^v)>=c[i] || z){
							j=x|(u<a[i]); k=y|(v<b[i]); l=z|((u^v)>c[i]);
							tmp=(ll)g[i-1][j][k][l]*((u^v)-c[i])*bin[i-1]%p;
							if (tmp<0) tmp+=p; ad(tmp,f[i-1][j][k][l]);
							ad(f[i][x][y][z],tmp);
							ad(g[i][x][y][z],g[i-1][j][k][l]);
						}
		printf("%d\n",f[len][0][0][0]);
	}
	return 0;
}


by lych

2016.4.21

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值