题目描述
分析
呐首先这个布尔函数的变量只能是0,1.反正比赛的时候我以为可以随便取,就不会做了。
我们定义(v1,1 or v,1,2)为一个项,x为变量。
先观察题目,发现要项里面最多只有两个东西。然后里面的东西相同的不会出现两次以上,这是什么意思呢?意味着一个变量至多出现在两个不同的项里面。
纯暴力是对变量枚举,由于上面的特殊性,又由一般的xor套路可得,我们应该可以转化成对异或的项dp。
考虑subtask2,我们直接顺序对每一项DP就行了,因为一个项里面的变量在别的项中不会出现,那么我们这时枚举情况是无后效性的嘛。
而subtask3,可以状压DP来解决。
剩下的分怎么拿?观察每个变量最多在两个项出现这一条件。可以得出:我们确定了一个变量,只会影响至多两个项。那么我们先枚举一个变量x1的情况,影响了两个项,若再枚举两个项的其中一个项的另一个变量xi,有一个项就确定了,这时候另一个包含xi的项就又被影响了。然而我们发现可以保持被影响而不确定的项保持在2个以内,而且确定的项只有xor起来的值是有用的,要维护的信息非常少,是不是可做了呢?
不断地划水后,终于学会了游泳,这个我们可以进行图的转化!把项看成点,定义两个点有一条边相连,为拥有一对相同相同的变量。这样的话转化后就变成一堆链和环了。我们只要对每个环和链顺着边DP就好了。这里讨论环。(链其实是一样的)
若一个环有n个点,设F[i][init][last][val]表示某条环做到第i个点,第一个点和第n个点的共同变量值为init,第i个点和i+1共同的变量值为last,前i个点xor值为val。就可以搞出一个环值是0和1的方案数了。
最后合并一下,输出1的方案就好了。
还要考虑自环和未出现的变量的影响。
总结:这道题最主要是发现各种特殊性,然后图的转化。当然理解题意也很重要····
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=100005;
const int mo=1000000007;
int n,i,j,k[N],a[N][3],m,l,x[N],tmp,tp,f[N][70][2],ref[N],b[N],met[N],cnt[N];
ll ans;
int orit(int val,int tar,int pos)
{
if (tar) tar=1;
if (pos>0) return val|tar;
else return val|(!tar);
}
void dfs(int x,int y,int z)
{
if (x>k[i+1])
{
f[i+1][z][l^y]+=f[i][j][l];
if (f[i+1][z][l^y]>mo) f[i+1][z][l^y]-=mo;
return;
}
int now=abs(a[i+1][x]);
if (cnt[now]>1&&met[now])
dfs(x+1,orit(y,(1<<(ref[now]-1))&j,a[i+1][x]),z);
else
{
int add=0;
if (cnt[now]>1) add=1<<(ref[now]-1);
dfs(x+1,orit(y,1,a[i+1][x]),z+add);
dfs(x+1,orit(y,0,a[i+1][x]),z);
}
}
int main()
{
freopen("equation.in","r",stdin);
// freopen("equation.out","w",stdout);
scanf("%d %d",&n,&m);
fo(i,1,n)
{
scanf("%d",k+i);
fo(j,1,k[i])
{
scanf("%d",a[i]+j);
cnt[abs(a[i][j])]++;
}
}
if (m<=21)
{
fo(i,0,(1<<m)-1)
{
fo(j,1,m)
if (i&(1<<(j-1))) x[j]=1;
else x[j]=0;
tmp=0;
fo(j,1,n)
{
tp=0;
fo(l,1,k[j])
{
if (a[j][l]>0)
tp|=x[a[j][l]];
else tp|=!(x[-a[j][l]]);
}
tmp^=tp;
}
if (tmp) ans++;
}
}
else
{
fo(i,1,m) if (cnt[i]>1)
{
b[++b[0]]=i;
ref[i]=b[0];
}
f[0][0][0]=1;
fo(i,0,n-1)
{
fo(j,0,(1<<b[0])-1)
fo(l,0,1)
if (f[i][j][l])
{
dfs(1,0,j);
}
fo(j,1,k[i+1]) met[abs(a[i+1][j])]=1;
}
fo(j,0,(1<<b[0])-1) ans=(ans+f[n][j][1])%mo;
fo(i,1,m) if (!met[i]) ans=ans*2%mo;
}
printf("%lld",ans);
return 0;
}