【HAOI2018】反色游戏

题面

https://www.luogu.org/problem/P4494

题解

$2018$蛤省省选神仙题。

前置知识:线性基。$orz\ aysn$

关键的一个结论:在一个任意的联通块内,如果它的黑点个数为偶数个,则“联通块内的目标”是可以达到的。设点数为$n$,边数为$m$,则方案数为$2^{m-n+1}$,这个结论的证明需要线性基,将任意一个生成树内的边全部插入线性基,就构成了一个可以表达任意数的线性基,可以证明,加入其他的边是冗杂的,无论线性基外的边怎么选,线性基里面的边总可以使他变回全$0$的状态。如果任意一个线性基中的边不按它“应该”在的状态,都是不行的。当黑点的个数为奇数个时,是无法通过异或得到的。

所以直接$tarjan$一发就好了,三个判断分别是判断去掉$x$后下面的点双是不是奇数个黑点、其他联通块是不是奇数个黑点、$x$上面的点双是不是奇数个黑点。

突然想起来刘汝佳紫书上的一道题,和异或有关的,是研学游在大巴上想的,也许和这个有关吧。

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100500
#define mod 1000000007
#define ri register int

using namespace std;

inline int read() {
  int ret=0; char ch=getchar();
  while (ch<'0' || ch>'9') ch=getchar();
  while (ch>='0' && ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar();
  return ret;
}

vector<int> to[N];
char ch[N];
int n,m,a[N],mi[N<<1];
int dfn[N],low[N],vis[N],clc,rt;
int sum[N],c[N],d[N],f[N],dg[N];

void init() {
  clc=0;
  for (ri i=1;i<=n;i++) dfn[i]=low[i]=d[i]=c[i]=f[i]=0;
  for (ri i=1;i<=n;i++) to[i].clear();
}

void tarjan(int x) {
  dfn[x]=low[x]=++clc; vis[x]=rt; sum[x]=a[x];
  for (ri i=0;i<to[x].size();i++) {
    int y=to[x][i];
    if (!dfn[y]) {
      tarjan(y); low[x]=min(low[x],low[y]);
      sum[x]+=sum[y];
      if (low[y]>=dfn[x]) {
        d[x]|=(sum[y]&1),c[x]++,f[x]+=sum[y];
      }
    }
    else low[x]=min(low[x],dfn[y]);
  }
  if (x==rt) c[x]--;
}

int main() {
  mi[0]=1;
  for (ri i=1;i<(N<<1);i++) mi[i]=(mi[i-1]<<1)%mod;
  int T=read();
  while (T--) {
    n=read();m=read();init();
    int val=0,tot=0;
    for (ri i=1;i<=m;i++) {
      int u=read(),v=read();
      to[u].push_back(v);
      to[v].push_back(u);
    }
    for (ri i=1;i<=n;i++) dg[i]=to[i].size();
    scanf("%s",ch+1);
    for (ri i=1;i<=n;i++) a[i]=ch[i]-'0';
    for (ri i=1;i<=n;i++) if (!dfn[i]) {
      rt=i;
      tarjan(i);
      ++tot;
      val+=sum[i]&1;
    }
    printf("%d ",val?0:mi[m-n+tot]);
    for (ri i=1;i<=n;i++) {
      if (d[i]) printf("0 ");
      else if (val-(sum[vis[i]]&1)) printf("0 ");
      else if ((sum[vis[i]]-a[i]-f[i])&1) printf("0 ");
      else printf("%d ",mi[m-n+tot-dg[i]+1+c[i]]);
    }
    puts("");
  }
  return 0;
}

 

posted @ 2019-08-30 16:17  HellPix  阅读(254)  评论(0编辑  收藏  举报