Codeforces 704C

很恶心人的题。
我们考虑把每个变量当做无向边,那么每个点的度数至多是 2 2 2。因此每个连通块忽略自环后都是链或环,都可以用DP求出来答案。大概对于链的话,记录 F [ i ] [ j = 0 / 1 ] [ k = 0 / 1 ] F[i][j=0/1][k=0/1] F[i][j=0/1][k=0/1]表示考虑到链的第 i i i个位置,上个变量的取值为 j j j,目前已经确定的子句的异或和为 k k k,转移直接枚举下一个变量的取值即可。环的话找一个变量破开就行了。
注意特判自环或者 k i = 1 k_i=1 ki=1的变量,WA爆了。
时间复杂度 O ( n ) \mathcal O(n) O(n)

#include <bits/stdc++.h>
#define MOD 1000000007

using namespace std;

typedef long long ll;

inline void add(int &x,int y) {
  ((x+=y)>=MOD)?x-=MOD:0;
}

struct Edge {
  int t,next;
  bool v;
  Edge() {}
  Edge(int a,bool b,int c):t(a),v(b),next(c) {}
};

Edge e[200005];
int head[100005],d[100005],tot;

bool vis1[100005],vis2[100005],tag[100005];
vector <int> bel[100005];

bool odd[100005];
int ans[2];

inline void addEdge(int x,int y,bool v) {
  e[++tot]=Edge(y,v,head[x]);
  head[x]=tot;
  e[++tot]=Edge(x,v,head[y]);
  head[y]=tot;
  d[x]++;d[y]++;
}

inline void merge(int *p) {
  int sum[2]={0,0};
  for(int t1=0;t1<2;t1++)
    for(int t2=0;t2<2;t2++) add(sum[t1^t2],(ll)ans[t1]*p[t2]%MOD);
  ans[0]=sum[0];ans[1]=sum[1];
}

int q[100005];
bool val[100005];

void dp_chain(int n) {
  int f[2][2][2];
  int cur=0;
  memset(f[cur],0,sizeof(f[cur]));
  f[cur][0][0]=1;
  if (odd[q[1]]) f[cur][0][1]=1;
  for(int i=1;i<n;i++) {
  	int x=q[i];
  	cur^=1;
  	memset(f[cur],0,sizeof(f[cur]));
  	for(int t1=0;t1<2;t1++)
  	for(int t2=0;t2<2;t2++)
  	  if (f[cur^1][t1][t2]) {
  	  	  for(int t3=0;t3<2;t3++) 
			add(f[cur][t1^(t2|t3)][t3^val[i]],f[cur^1][t1][t2]);
		}
  }
  int sum[2]={0,0};
  if (odd[q[n]]) {
  	for(int t1=0;t1<2;t1++)
  	for(int t2=0;t2<2;t2++)
  	  if (f[cur][t1][t2]) {
  	  	  for(int t3=0;t3<2;t3++)
  	  	    add(sum[t1^(t2|t3)],f[cur][t1][t2]);
		}
  }
  else {
  	for(int t1=0;t1<2;t1++)
  	for(int t2=0;t2<2;t2++)
  	  if (f[cur][t1][t2]) add(sum[t1^t2],f[cur][t1][t2]);
  }
  merge(sum);
}

void dp_cir(int n) {
  int f[2][2][2][2];
  int cur=0;
  memset(f[cur],0,sizeof(f[cur]));
  f[cur][0][0][0]=f[cur][0][1][1]=1;
  for(int i=1;i<n;i++) {
  	int x=q[i];
  	cur^=1;
  	memset(f[cur],0,sizeof(f[cur]));
  	for(int t1=0;t1<2;t1++)
  	for(int t2=0;t2<2;t2++)
  	for(int t3=0;t3<2;t3++)
  	  if (f[cur^1][t1][t2][t3]) {
  	  	  for(int t4=0;t4<2;t4++)
  	  	    add(f[cur][t1^(t2|t4)][t4^val[i]][t3],f[cur^1][t1][t2][t3]);
		}
  }
  int sum[2]={0,0};
  for(int t1=0;t1<2;t1++)
  for(int t2=0;t2<2;t2++)
  for(int t3=0;t3<2;t3++)
    if (f[cur][t1][t2][t3]) add(sum[t1^(t2|(t3^val[n]))],f[cur][t1][t2][t3]);
  merge(sum);
}

int find_path(int s) {
  int cnt=0,x=s;
  do {
    q[++cnt]=x;
    vis1[x]=1;
    int u=0;
    for(int i=head[x];i;i=e[i].next)
      if (!vis2[(i+1)>>1]) {
      	u=e[i].t;
      	vis2[(i+1)>>1]=1;
      	val[cnt]=e[i].v;
      	break;
	  }
	x=u;
  } while (x&&x!=s);
  return cnt;
}

vector <int> vt[100005];

int main() {
  ans[0]=1;
  int n,m;
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++) {
  	int x;
  	scanf("%d",&x);
  	vt[i].resize(x);
  	for(int j=0;j<x;j++) {
  		scanf("%d",&vt[i][j]);
  		bel[abs(vt[i][j])].push_back(i);
  		tag[abs(vt[i][j])]^=(vt[i][j]<0);
	  }
  }
  for(int i=1;i<=m;i++)
    if (!bel[i].size()) ans[0]=ans[0]*2LL%MOD;
    else if (bel[i].size()==1) odd[bel[i][0]]=1;
    else if (bel[i][0]!=bel[i][1]) addEdge(bel[i][0],bel[i][1],tag[i]);
  for(int i=1;i<=n;i++)
    if (!d[i]) {
    	int sum[2]={0,0};
    	if (vt[i].size()==1) sum[0]=sum[1]=1;
		else if (abs(vt[i][0])!=abs(vt[i][1])) sum[0]=1,sum[1]=3;
		else if ((vt[i][0]>0)^(vt[i][1]>0)) sum[1]=2;
		else sum[0]=sum[1]=1;
		merge(sum);
	}
	else if (d[i]==1&&!vis1[i]) {
		int cnt=find_path(i);
		dp_chain(cnt);
	}
  for(int i=1;i<=n;i++)
    if (d[i]==2&&!vis1[i]) {
    	int cnt=find_path(i);
    	dp_cir(cnt);
	}
  printf("%d\n",ans[1]);
  return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值