代码等式[POI]

[题目大意]

给你两个由小写字母和0、1组成的字符串。每一个小写字母都代表一个长度已知的01串,问能使这两个字符串相同的解有多少个。比如已知字母a的长度为10000,给出的两个字符串分别为a和1,则解一共有2^10000个。

[分析]

因为每个小写字母对应的01串的长度都已知,所以可以将每个小写字母对应的01串拆开,及a1a2…an,b1b2b3…bn的形式。因为两个字符串相同,所以它们对应位置的两个字符必然相同。我们把它们连一条边,最后就能形成数个联通块。我们只要统计出不含0、1,及不确定是0还是1的联通块的个数k,就可以计算出答案2^k。因为这道题目解的个数比较大,所以不要忘记高精度哦。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

struct bign{
    int num[10000],len;
    bign(int a=0){
        memset(num,0,sizeof(num));
        len=1;
		num[len]=a;
    }
    bign operator = (const char *str){
    	len=strlen(str);
    	for(int i=1;i<=len;i++)num[i]=str[len-i]-'0';
    	return *this;
	} 
    bign operator = (const int a){
    	char str[10000];
    	sprintf(str,"%d",a); 
    	*this=str;
    	return *this;
	}
    bign operator *=(const int a){
    	int b=0;
        for(int i=1;i<=len+1;i++){ 
            num[i]=num[i]*a+b;
            b=num[i]/10;
            if(num[i]>=10){
                num[i]%=10;
            }
        }
        if(num[len+1])len++;
        return *this;
    }
    void print(){
        for(int i=len;i>=1;i--)printf("%d",num[i]);
    }
};

struct point{
	bool is_number;
	int ler,inx,number;
	point(int x=0,int y=0):ler(x),inx(y),is_number(false),number(0){}
	bool operator != (const point &b)const{
		return ((is_number!=b.is_number)||(!is_number&&!b.is_number&&(ler!=b.ler||inx!=b.inx))||(is_number
				&&b.is_number&&number!=b.number));
	}
	bool operator == (const point &b)const{
		return !((*this)!=b);
	}
};

int t,n;
int l1,l2;
char s1[10010],s2[10010];
int num[10010];
bool vis[30][10010];
point six[30][10010];
point l[100100],r[100100];

void calm(){
	printf("0\n");
}

bign power(int a,int b){
	bign ans=1;
	for(int i=1;i<=b;i++){
		ans*=2;
	}
	return ans;
}

void init(){
	memset(vis,0,sizeof vis);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&num[i]);
	scanf("%d%s%d%s",&l1,s1,&l2,s2);
	for(int i=0;i<=1;i++)six[0][i].ler=0,six[0][i].inx=i,six[0][i].is_number=true,six[0][i].number=i;
	for(int i=1;i<=n;i++)for(int j=1;j<=num[i];j++)six[i][j]=point(i,j);
}

point find(point x){
	point tmp=x,pre;
	while(tmp!=six[tmp.ler][tmp.inx])tmp=six[tmp.ler][tmp.inx];
	while(x!=tmp){
		pre=six[x.ler][x.inx];
		six[x.ler][x.inx]=tmp;
		x=pre;
	}
	return tmp;
}

void work(){
	int r1=0,r2=0;
	for(int i=0;i<l1;i++){
		if(s1[i]=='0'||s1[i]=='1')l[++r1].is_number=true,l[r1].number=s1[i]-'0',l[r1].ler=0,l[r1].inx=s1[i]-'0';
		else {
			for(int j=1;j<=num[s1[i]-'a'+1];j++)
				l[r1+j].is_number=false,l[r1+j].ler=s1[i]-'a'+1,l[r1+j].inx=j;
			r1+=num[s1[i]-'a'+1];
		}
	}
	for(int i=0;i<l2;i++){
		if(s2[i]=='0'||s2[i]=='1')r[++r2].is_number=true,r[r2].number=s2[i]-'0',r[r2].ler=0,r[r2].inx=s2[i]-'0';
		else {
			for(int j=1;j<=num[s2[i]-'a'+1];j++)
				r[r2+j].is_number=false,r[r2+j].ler=s2[i]-'a'+1,r[r2+j].inx=j;
			r2+=num[s2[i]-'a'+1];
		}
	}
	if(r1!=r2){calm();return;}
	for(int i=1;i<=r1;i++){
		point rl=find(l[i]),rr=find(r[i]);
		if(rl==rr)continue;
		if(rl.is_number&&rr.is_number&&rl.number!=rr.number){calm();return;}
		else if(!rl.is_number&&!rr.is_number)six[rr.ler][rr.inx]=rl;
		else if(rl.is_number&&!rr.is_number){six[rr.ler][rr.inx].is_number=true;six[rr.ler][rr.inx].number=rl.number;}
		else {six[rl.ler][rl.inx].is_number=true;six[rl.ler][rl.inx].number=rr.number;}
	}
	int tot=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=num[i];j++){
			point tmp=find(six[i][j]);
			if(!tmp.is_number&&!vis[tmp.ler][tmp.inx]){
				vis[tmp.ler][tmp.inx]=true;
				tot++;
			}
		}
	power(2,tot).print();
	printf("\n");
}

int main(){
	freopen("row.in","r",stdin);
	freopen("row.out","w",stdout);
	scanf("%d",&t);
	for(int i=1;i<=t;i++){
		init();
		work();
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值