Description
计算给定范围内有多少种输入可以使输出为1。 我们假设3 < n < 100, 3 < m < 3000,而且网络中的门是用1到m之间的数任意编号的。
Input
文件第一行包含三个整数,分别表示输入的个数,门的个数,连接到输出的门的编号。以下的m行描述网络中的连接情况。第I行表示第I个门的两个输入,两个输入为范围[-n,m]之间的一个整数。如果输入是网络的第k个输入,则连接的描述是一个整数-k,如果输入是第j个门,则连接的描述是一个整数j。以下两行各有一个n位二进制串,表示输入的上限和下限。
Output
包含一个整数,表示给定范围内有多少种输入可以使输出为1。
由异或的性质,拓扑排序并处理出最终输出等于哪些输入异或起来,于是转成区间内有几个二进制数和指定数的按位与有奇数个1,可以数位dp
#include<cstdio> #include<bitset> typedef unsigned long long u64; int n,m,o; int es[6007],enx[6007],e0[3007],deg[3007],q[3007],ql=0,qr=0,ep=2; std::bitset<102>st[3007]; int S[107]; char l[107],r[107]; void ae(int a,int b){ es[ep]=b;enx[ep]=e0[a];e0[a]=ep++; ++deg[b]; } struct num{ u64 a,b; num(u64 _a,u64 _b):a(_a),b(_b){}; num(u64 _a=0):a(0),b(_a){} void print(){ int ss[107],sp=0; u64 v1=a>>32,v2=a&0xffffffffll; u64 v3=b>>32,v4=b&0xffffffffll; while(v1|v2|v3|v4){ v2+=v1%10<<32;v1/=10; v3+=v2%10<<32;v2/=10; v4+=v3%10<<32;v3/=10; ss[sp++]=v4%10;v4/=10; } if(!sp)putchar('0'); while(sp)putchar('0'+ss[--sp]); } }; num operator+(num x,num y){ u64 c=x.b+y.b; return num(x.a+y.a+(c<x.b),x.b+y.b); } num operator-(num x,num y){ return num(x.a-y.a-(x.b<y.b),x.b-y.b); } num f[107][2][2]; void pre(){ f[n+1][0][0]=1; for(int i=n;i;--i){ f[i][0][0]=f[i][1][S[i]]=f[i+1][0][0]+f[i+1][1][0]; f[i][0][1]=f[i][1][S[i]^1]=f[i+1][0][1]+f[i+1][1][1]; } } num cal(char*s,bool e){ for(int i=1;i<=n;++i)s[i]-='0'; int d=0; num ans=0; for(int i=1;i<=n;++i){ if(s[i])ans=ans+f[i][0][d^1]; if(S[i])d^=s[i]; } if(e){ int d=0; for(int i=1;i<=n;++i)if(S[i])d^=s[i]; ans=ans+d; } return ans; } int main(){ scanf("%d%d%d",&n,&m,&o); for(int i=1,a,b;i<=m;++i){ scanf("%d%d",&a,&b); if(a<0)st[i].flip(-a); else ae(a,i); if(b<0)st[i].flip(-b); else ae(b,i); } for(int i=1;i<=m;++i)if(!deg[i])q[++qr]=i; while(ql!=qr){ int w=q[++ql]; for(int i=e0[w];i;i=enx[i]){ int u=es[i]; st[u]^=st[w]; if(!--deg[u])q[++qr]=u; } } for(int i=1;i<=n;++i)S[i]=st[o][i]; scanf("%s%s",l+1,r+1); pre(); (cal(r,1)-cal(l,0)).print(); return 0; }