zoukankan      html  css  js  c++  java
  • [BZOJ5303][HAOI2018]反色游戏(Tarjan)

    暴力做法是列异或方程组后高斯消元,答案为2^自由元个数,可以得60分。但这个算法已经到此为止了。

    从图论的角度考虑这个问题,当原图是一棵树时,可以从叶子开始唯一确定每条边的选择情况,所以答案为1。

    于是首先,对一个连通块,若其中黑点个数为奇数则必然无解,否则考虑求出它的一棵生成树。然后当我们选择一条非树边(u,v)时,只需要将树上u,v两点间的所有边的选取情况取反,就又得到一个合法方案。于是答案为$2^{m-n+1}$。进一步可以发现,设原图连通块个数为c,则答案为$2^{m-n+c}$。

    现在考虑删去一个点后的改变:

    若此点不是割点,则根据它的颜色更新有奇数个黑点的连通块个数,直接输出即可。

    若此点是割点,那么还要考虑删去它后,被分割后的几个连通块中是否出现了有奇数个黑点的连通块。

    那么,对于每个割点,我们需要计算(其中“之后”是指DFS序在i之后):w[i]这个点及之后的点中黑点的个数,d[i]删去这个点后之后的被分割出的连通块中是否存在有奇数个黑点的,f[i]删去这个点后会新增多少个连通块(对根特判)。最后分情况讨论无解即可。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
     5 using namespace std;
     6 
     7 const int N=100010,mod=1e9+7;
     8 char ch;
     9 int T,n,m,u,v,s[N],w[N],b[N],dfn[N],low[N],f[N],d[N],ind[N],bin[N];
    10 int cnt,ID,tim,ans,c,h[N],to[N<<1],nxt[N<<1];
    11 
    12 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
    13 void init(){ cnt=tim=c=0; rep(i,1,n) dfn[i]=f[i]=d[i]=ind[i]=h[i]=0; }
    14 
    15 void Tarjan(int x){
    16     b[x]=ID; dfn[x]=low[x]=++tim;
    17     For(i,x) if (!dfn[k=to[i]]){
    18         Tarjan(k); low[x]=min(low[x],low[k]); w[x]+=w[k];
    19         if (low[k]>=dfn[x]) d[x]|=w[k]&1,f[x]++;
    20     }else low[x]=min(low[x],dfn[k]);
    21     if (x==ID) f[x]--;
    22 }
    23 
    24 int main(){
    25     freopen("game.in","r",stdin);
    26     freopen("game.out","w",stdout);
    27     bin[0]=1; rep(i,1,200000) bin[i]=(bin[i-1]<<1)%mod;
    28     for (scanf("%d",&T); T--; ){
    29         scanf("%d%d",&n,&m); init(); ans=m-n;
    30         rep(i,1,m) scanf("%d%d",&u,&v),add(u,v),add(v,u),ind[u]++,ind[v]++;
    31         rep(i,1,n) scanf(" %c",&ch),w[i]=s[i]=ch-'0';
    32         rep(i,1,n) if (!dfn[i]) ID=i,Tarjan(i),ans++,c+=w[i]&1;
    33         printf("%d ",c ? 0 : bin[ans]);
    34         rep(i,1,n){
    35             if (d[i]) printf("0 ");
    36             else if (c-(w[b[i]]&1)) printf("0 ");
    37                 else if ((w[b[i]]-s[i])&1) printf("0 ");
    38                     else printf("%d ",bin[ans-ind[i]+1+f[i]]);
    39         }
    40         puts("");
    41     }
    42     return 0;
    43 }
  • 相关阅读:
    repo
    manifest
    Gerrit使用简介
    id_rsa id_rsa.pub
    数字签名原理及其应用
    RSA DSA
    ssh(安全协议外壳)
    Numpy基本数据结构
    Numpy
    .bat 批处理
  • 原文地址:https://www.cnblogs.com/HocRiser/p/10354033.html
Copyright © 2011-2022 走看看