zoukankan      html  css  js  c++  java
  • HDU 5803 Zhu’s Math Problem 2016多校第六场1011 数位dp

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5803
    题意:在a<=A,b<=B,c<=C,d<=D下,找出符合a+c>b+d 并且 a+d≥b+c的所有(a,b,c,d)。
    题解:由于A,B,C,D范围太大,可直接考虑数位dp。为什么说可以直接考虑数位dp呢?当数位dp做多的时候,会发现数位dp就一个套路。就是在搜索的时候是一位一位地考虑,那么当我们得到两个k长度的数A,B时,如果A,B状态相同,就可以只搜一个,另外一个直接回溯。为什么这样?我们考虑以什么作为状态的时候,就直接考虑相同长度的两个数A,B,如果A,B某一种特征相同时,那么剩下位数添加数字能得到相同的结果,我们就把这个特征作为状态。举个例子,比如说我们要找区间内不包含连续49的数字的个数。当我们得到同数位的468和247时,显然,468和247再增加一位时,468和247都可以在下一位添加0~9任意数字(非限制情况)。
    回到本题,我们考虑上述条件的时候,令f1=a+c-b-d,f2=a+d-b-c,我们知道当相同数位不同数字的f1,f2相同时,那么这两个状态就是等价的。那么直接存这个状态是不行,仔细考虑一下,会发现在做减法时,只有相邻的两位才会才产生影响,那么f1,f2只要存下前一位的结果就行。用二进制优化(黑科技啊)。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int mod=1e9+7;
    int T;
    ll p[4];
    int hi[3]={-2,0,2};
    int dp[65][1<<4][4][4];
    
    inline void modify(int& x,int y)
    {
    	x+=y;
    	if(x>=mod) x-=mod;
    }
    
    inline int carry(int x,int f)
    {
    	if(f==3) return 3;
    	int res=x+hi[f];
    	if(res<-1) return -1;
    	if(res>1) return 3;
    	return res+1;
    }
    
    int dfs(int pos,int limit,int f1,int f2)
    {
    	if(pos<0) return f1>=2&&f2>=1;
    	if(dp[pos][limit][f1][f2]!=-1) return dp[pos][limit][f1][f2];
    	int ed[4];
    	int& ans=dp[pos][limit][f1][f2]=0;
    	for(int i=0;i<4;i++) ed[i]=limit>>i&1?1:p[i]>>pos&1;
    	for(int i=0;i<=ed[0];i++)
    		for(int j=0;j<=ed[1];j++)
    			for(int k=0;k<=ed[2];k++)
    				for(int z=0;z<=ed[3];z++)
    				{
    					int newf1=carry(i+k-j-z,f1),newf2=carry(i+z-j-k,f2),newlimit=limit;
    					if(newf1==-1||newf2==-1) continue;
    					if(i<ed[0]) newlimit|=1;
    					if(j<ed[1]) newlimit|=2;
    					if(k<ed[2]) newlimit|=4;
    					if(z<ed[3]) newlimit|=8;
    					modify(ans,dfs(pos-1,newlimit,newf1,newf2));
    				}
    	return ans;
    }
    
    int main()
    {
    	scanf("%d",&T);
    	while(T--)
    	{
    		memset(dp,-1,sizeof(dp));
    		scanf("%I64d%I64d%I64d%I64d",&p[0],&p[1],&p[2],&p[3]);
    		printf("%d
    ",dfs(61,0,1,1));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    吃推荐3个最近去了的好地方
    30岁生日
    今天开始试用Briglow Hair Cream
    WPF中如何在文本外面加虚线外框
    对账算法改进
    如何退出正在Sleep的线程
    关于framework4.5的相关介绍
    恐怖的报警邮件
    对帐引擎2个月后的监控数据
    wcf rest服务启用gzip压缩
  • 原文地址:https://www.cnblogs.com/zhuyutian/p/5818747.html
Copyright © 2011-2022 走看看