[环 链 DP] Hillan模拟赛 B.或异或



因为每个点不超过两次 两两连边 发现是一些环和链相互独立

然后就是在环链上DP 注意一些细节


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int P=100000007;
const int N=100005;

struct edge{
  int u,v,next;
}G[N<<2];
int head[N],inum=1;

inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int tag[N],tis[N];

int vst[N];
#define V G[p].v

int cir,tot,lst[N];

inline void dfs(int u,int fa){
  vst[u]=1; lst[++tot]=u;
  for (int p=head[u];p;p=G[p].next)
    if (p!=(fa^1)){
      if (vst[V])
	cir=1;
      else
	dfs(V,p);
    }
}

ll f[N][2][2][2];

int cnt; ll F[N][2];

inline void Point(int idx){
  int x=lst[1];
  if (tis[x]==1)
    F[idx][0]=F[idx][1]=1;
  else if (tag[x]==0)
    F[idx][0]=2;
  else
    F[idx][1]=2;
}

inline void Chain(int idx){
  f[1][0][0][0]=f[1][1][1][0]=1;
  for (int i=2;i<=tot;i++){
    int x=lst[i];
    for (int t=0;t<2;t++)
      for (int o=0;o<2;o++)
	for (int k=0;k<2;k++)
	  for (int j=0;j<2;j++)
	    (f[i][t][j^tag[x]][k^(o|j)]+=f[i-1][t][o][k])%=P;
  }
  for (int t=0;t<2;t++)
    for (int o=0;o<2;o++)
      for (int k=0;k<2;k++){
	int ret=k;
	if (tis[lst[1]]==2)
	  ret^=(t^tag[lst[1]]);
	if (tis[lst[tot]]==2)
	  ret^=o;
	(F[idx][ret]+=f[tot][t][o][k])%=P;
      }
  for (int i=1;i<=tot;i++) for (int t=0;t<2;t++) for (int o=0;o<2;o++) for (int k=0;k<2;k++) f[i][t][o][k]=0;
}

inline void Circle(int idx){
  lst[++tot]=lst[1];
  f[1][0][0][0]=f[1][1][1][0]=1;
  for (int i=2;i<=tot;i++){
    int x=lst[i];
    for (int t=0;t<2;t++)
      for (int o=0;o<2;o++)
	for (int k=0;k<2;k++)
	  for (int j=0;j<2;j++)
	    (f[i][t][j^tag[x]][k^(o|j)]+=f[i-1][t][o][k])%=P;
  }
  F[idx][0]=(f[tot][0][0][0]+f[tot][1][1][0])%P;
  F[idx][1]=(f[tot][0][0][1]+f[tot][1][1][1])%P;
  for (int i=1;i<=tot;i++) for (int t=0;t<2;t++) for (int o=0;o<2;o++) for (int k=0;k<2;k++) f[i][t][o][k]=0;
}

ll H[N][2];

inline ll Total(){
  H[0][0]=1;
  for (int i=1;i<=cnt;i++)
    for (int j=0;j<2;j++)
      for (int k=0;k<2;k++)
	(H[i][j^k]+=H[i-1][j]*F[i][k]%P)%=P;
  return H[cnt][1];
}

int n,m;
int deg[N];

int main(){
  int b,tem,last;
  freopen("orxor.in","r",stdin);
  freopen("orxor.out","w",stdout);
  read(m); read(n);
  for (int i=1;i<=m;i++){
    read(b);
    for (int j=1;j<=b;j++){
      last=tem; read(tem);
      if (tem<0) tem=-tem,tag[tem]^=1;
      tis[tem]++;
    }
    if (b==2)
      add(tem,last,++inum),add(last,tem,++inum),deg[tem]++,deg[last]++;
  }
  ll tt=1;
  for (int i=1;i<=n;i++)
    if (!tis[i])	
      (tt*=2)%=P;
  for (int i=1;i<=n;i++)
    if (tis[i] && !vst[i]){
      cir=tot=0;
      dfs(i,0);
      if (!cir){
	for (int j=1;j<=tot;j++)
	  vst[lst[j]]=0;
	for (int j=1;j<=tot;j++)
	  if (deg[lst[j]]==1){
	    tot=0,dfs(lst[j],0);
	    break;
	  }
      }
      ++cnt;
      if (tot==1)
	Point(cnt);
      else if (!cir)
	Chain(cnt);
      else
	Circle(cnt);
    }
  printf("%lld\n",tt*Total()%P);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值