bzoj 3780: 数字统计 (数位dp)

3780: 数字统计

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 50  Solved: 27
[ Submit][ Status][ Discuss]

Description

小A正在研究一些数字统计问题。有一天他突然看到了一个这样的问题:
将[L..R]中的所有整数用M位二进制数表示(允许出现前导0)。现在将这些数中的每一个作如下变换:
从这个数的最低两位开始,如果这两位都是0,那么X=1,否则X=0。现在将这两位删去,然后将X放在原来最低位的位置上。重复这个变换直到这个数只剩下一位为止。
例如01001的变换过程如下:
01001-->0100-->011-->00-->1。
现在的问题是变换后的所有数中,值为Y(Y为0或1)的有多少个?
小A不会了,他想让你帮助他完成这个问题。

Input

输入文件包含多组测试数据。
第一行,一个整数T,表示测试数据的组数。
接下来的T节,每节对应一组测试数据,格式如下:
第一行,两个整数M、Y。
第二行,两个M位二进制数L、R。

Output

对于每组测试数据,输出一行,一个二进制数,表示该组测试数据中[L..R]中的所有整数变换后的值为Y的个数。这里的二进制数不允许出现前导0。

Sample Input

1
3 1
001 101

Sample Output

11

HINT

对于全部的数据,1<=M<=200,1<=T<=50。


Source

[ Submit][ Status][ Discuss]

题解:数位dp

这道题要写二进制高精哦。。。

我们先看上面的变换,正常来看应该是从低位向高位填,每次将填好的最后两个变成他要求的0或1,但是这样推没法控制上下界。那么倒着看上面的变换过程00->1 这个其实第一位的0就是原数的0,那么也就是每次由一位变成两位,这两位中的第一个一定是与原数一致的,第二个表示的是下面两位的变换。

f[i][j][0/1][0/1]表示的是确定到第i位,下面两位的变换结果为j,是否卡上下界。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 203
using namespace std;
int n,m;
int a[N],b[N],t,st[N],top,opt;
char s[N];
struct number{
	int s[300],len;
	number(){memset(s,0,sizeof(s));len=0;}
	number operator + (const number &a){
		number c;
		int t=max(a.len,len);
		for (int i=1;i<=t;i++)
		 c.s[i]=s[i]+a.s[i];
		for (int i=1;i<=t;i++) {
			c.s[i+1]+=(c.s[i]/2);
			c.s[i]%=2;
		}
		while (c.s[t+1]) {
			t++; 
			c.s[t+1]+=(c.s[t]/2);
			c.s[t]%=2;
		}
		c.len=t;
		return c;
	}
	number operator += (const number &a){
		*this=*this+a;
		return *this;
	}
}ans,f[N][3][3][3],one;
bool pd(int x,int y)
{
	if (x!=y||x==1&&y==1) return true;
	return false;
}
void solve(int x)
{
	int cnt=0;
	while (x) {
		st[++cnt]=x%2;
		x/=2;
	}
	for (int i=cnt;i>=1;i--) printf("%d",st[i]);
	printf("\n");
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&t);
	one.len=1;one.s[1]=1;
	for (int T=1;T<=t;T++) {
		scanf("%d%d",&n,&opt);
		scanf("%s",s+1); 
		for (int j=1;j<=n;j++) 
		 a[j]=s[j]-'0';
		scanf("%s",s+1);
		for (int j=1;j<=n;j++) 
		 b[j]=s[j]-'0';
		//for (int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<endl;
		//for (int i=1;i<=n;i++) cout<<b[i]<<" "; cout<<endl;
		memset(f,0,sizeof(f)); ans=number();
		if (!opt) {
			if (a[1]<=0&&b[1]>=0) f[1][1][a[1]==0?1:0][b[1]==0?1:0]+=one;//01
			if (a[1]<=1&&b[1]>=1) f[1][0][a[1]==1?1:0][b[1]==1?1:0]+=one;//10
			if (a[1]<=1&&b[1]>=1) f[1][1][a[1]==1?1:0][b[1]==1?1:0]+=one;//11
		}
		else {
		 if (a[1]!=0) {
		 	printf("0\n");
		 	continue;
		 }
		 f[1][0][a[1]==0?1:0][b[1]==0?1:0]+=one;
	    }
	    for (int i=1;i<=n-1;i++)
	     for (int j=0;j<=1;j++)
		    for (int a1=0;a1<=1;a1++)
		      for (int b1=0;b1<=1;b1++)
		        if (f[i][j][a1][b1].len!=0) {
		        	//cout<<i<<" "<<j<<" "<<a1<<" "<<b1<<" "<<f[i][j][a1][b1]<<endl;
		        	if (!j) {
		        		if (a1&&b1) {
		        		  if (a[i+1]<=0&&b[i+1]>=0) f[i+1][1][a[i+1]==0?1:0][b[i+1]==0?1:0]+=f[i][j][a1][b1];
			              if (a[i+1]<=1&&b[i+1]>=1) f[i+1][0][a[i+1]==1?1:0][b[i+1]==1?1:0]+=f[i][j][a1][b1];
			              if (a[i+1]<=1&&b[i+1]>=1) f[i+1][1][a[i+1]==1?1:0][b[i+1]==1?1:0]+=f[i][j][a1][b1];	
						}
						else if (a1) {
						  if (a[i+1]<=0) f[i+1][1][a[i+1]==0?1:0][0]+=f[i][j][a1][b1];
			              if (a[i+1]<=1) f[i+1][0][a[i+1]==1?1:0][0]+=f[i][j][a1][b1];
			              if (a[i+1]<=1) f[i+1][1][a[i+1]==1?1:0][0]+=f[i][j][a1][b1];
						}
						else if (b1) {
						  if (b[i+1]>=0) f[i+1][1][0][b[i+1]==0?1:0]+=f[i][j][a1][b1];
			              if (b[i+1]>=1) f[i+1][0][0][b[i+1]==1?1:0]+=f[i][j][a1][b1];
			              if (b[i+1]>=1) f[i+1][1][0][b[i+1]==1?1:0]+=f[i][j][a1][b1];
						}
						else {
						  f[i+1][1][0][0]+=f[i][j][a1][b1];
			              f[i+1][0][0][0]+=f[i][j][a1][b1];
			              f[i+1][1][0][0]+=f[i][j][a1][b1];
						}
					}
					else {
						if (a1&&b1) {
							 if (a[i+1]<=0&&b[i+1]>=0)  f[i+1][0][a[i+1]==0?1:0][b[i+1]==0?1:0]+=f[i][j][a1][b1];
						}
						else if (a1) {
							if (a[i+1]<=0) f[i+1][0][a[i+1]==0?1:0][0]+=f[i][j][a1][b1];
						}
						else if (b1) {
							if (b[i+1]>=0) f[i+1][0][0][b[i+1]==0?1:0]+=f[i][j][a1][b1];
						}
						else f[i+1][0][0][0]+=f[i][j][a1][b1];
						
				    }
			}
	    for (int i=0;i<=1;i++)
	      for (int a1=0;a1<=1;a1++)
	       for (int b1=0;b1<=1;b1++) 
		    {
			 if (a1&&b1) {
			   if (i>=a[n]&&i<=b[n]) ans+=f[n-1][i][a1][b1]; }
			 else if (a1) {
			 	if (i>=a[n]) ans+=f[n-1][i][a1][b1];
			 }
			 else if (b1) {
			 	if (i<=b[n]) ans+=f[n-1][i][a1][b1];
			 }
			 else ans+=f[n-1][i][a1][b1];
		    }
	    if (ans.len) {
	    	for (int i=ans.len;i>=1;i--) printf("%d",ans.s[i]);
	    	printf("\n");
		}
	    else printf("0\n");
	    //cout<<ans<<endl;
	}
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值