loj#2524bzoj5303 [Haoi2018]反色游戏(圆方树)

Posted quzhizhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了loj#2524bzoj5303 [Haoi2018]反色游戏(圆方树)相关的知识,希望对你有一定的参考价值。

  题目传送门:loj bzoj

  题意中的游戏方案可以转化为一个异或方程组的解,将边作为变量,点作为方程,因此若方程有解,方程的解的方案数就是2的自由元个数次方。我们观察一下方程,就可以发现自由元数量=边数-点数+连通块数,或者换句话说,若对原图的每个联通块指定一棵生成树,那么确定了生成树之外的边是否进行操作,那么生成树内的边的操作方案就是一定存在并唯一确定的。

  那么我们就只需要判断一下什么样的图无解。我们发现每对一条边进行操作,原图内的黑点数量奇偶性不变,那么我们只需判断图中的是否存在某个联通块有奇数个黑点,若存在即无解。

  加上了删点操作后,我们可以用圆方树来维护连通块信息。因为圆方树的连通性与原图上的连通性相互对应,删除单个点之后,原图被新分成的连通块就是圆方树删除对应点的连通块,那么使用圆方树就可以快速维护删除单个点的连通块信息。

  代码:

技术图片
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define mod 1000000007
#define maxn 200010
inline ll read()

    ll x=0; char c=getchar(),f=1;
    for(;c<0||9<c;c=getchar())if(c==-)f=-1;
    for(;0<=c&&c<=9;c=getchar())x=x*10+c-0;
    return x*f;

inline void write(ll x)

    static int buf[20],len; len=0;
    if(x<0)x=-x,putchar(-);
    for(;x;x/=10)buf[len++]=x%10;
    if(!len)putchar(0);
    else while(len)putchar(buf[--len]+0);

inline void writeln(ll x)write(x); putchar(\n);
inline void writesp(ll x)write(x); putchar( );
struct edge
    int to,nxt;
;
struct Graph
    edge e[4*maxn];
    int fir[2*maxn],deg[2*maxn];
    int tot;
    inline void clear()
    
        memset(fir,255,sizeof(fir)); tot=0;
        memset(deg,0,sizeof(deg));
    
    inline void add_edge(int x,int y)
    
        e[tot].to=y; e[tot].nxt=fir[x]; fir[x]=tot++;
        ++deg[x];
    
G,T;
int dfn[maxn],low[maxn],st[maxn],ans[maxn];
int val[2*maxn],size[2*maxn],fa[2*maxn],rt[2*maxn];
char s[maxn];
int n,m,tot,tp,cnt;
inline ll power(ll a,ll b)

    ll ans=1;
    for(;b;b>>=1,a=a*a%mod)
        if(b&1)ans=ans*a%mod;
    return ans;

void tarjan(int now,int last)

    dfn[now]=low[now]=++tot; st[++tp]=now;
    for(int i=G.fir[now];~i;i=G.e[i].nxt)
        if(i!=(last^1))
            if(!dfn[G.e[i].to])
                tarjan(G.e[i].to,i);
                low[now]=std::min(low[now],low[G.e[i].to]);
                if(low[G.e[i].to]>=dfn[now])
                    ++cnt;
                    T.add_edge(now,cnt); T.add_edge(cnt,now);
                    do
                        T.add_edge(st[tp],cnt); T.add_edge(cnt,st[tp]);
                    while(st[tp--]!=G.e[i].to);
                
            
            else low[now]=std::min(low[now],dfn[G.e[i].to]);
        

void dfs(int now,int root)

    rt[now]=root;
    size[now]=val[now];
    for(int i=T.fir[now];~i;i=T.e[i].nxt)
        if(T.e[i].to!=fa[now])
            fa[T.e[i].to]=now;
            dfs(T.e[i].to,root);
            size[now]+=size[T.e[i].to];
        

void work()

    n=read(); m=read();
    G.clear();
    for(int i=1;i<=m;i++)
        int x=read(),y=read();
        G.add_edge(x,y); G.add_edge(y,x);
    
    scanf("%s",s);
    memset(val,0,sizeof(val));
    for(int i=1;i<=n;i++)
        val[i]=(s[i-1]==1);
    T.clear();
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    tot=tp=0; cnt=n;
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i,-1);
            fa[i]=-1;
            dfs(i,i);
        
    int odd=0,block=0;
    for(int i=1;i<=n;i++)
        if(fa[i]==-1)odd+=(size[i]&1),++block;
    ans[0]=(odd?0:power(2,m-n+block));
    for(int i=1;i<=n;i++)
        odd-=(size[rt[i]]&1);
        int flag=1;
        for(int j=T.fir[i];~j;j=T.e[j].nxt)
            if(T.e[j].to!=fa[i]&&(size[T.e[j].to]&1))
                flag=0; break;
            
        if(odd||!flag||((size[rt[i]]-size[i])&1))ans[i]=0;
        else ans[i]=power(2,(m-G.deg[i])-(n-1)+(block+T.deg[i]-1));
        odd+=(size[rt[i]]&1);
    
    for(int i=0;i<=n;i++)
        writesp(ans[i]);
    putchar(\n);

int main()

    int T=read();
    while(T--)work();
    return 0;
反色游戏

 

以上是关于loj#2524bzoj5303 [Haoi2018]反色游戏(圆方树)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2425 [HAOI2010]计数 数位dp

loj2061 「HAOI2016」放棋子

loj2062 [HAOI2016]地图

loj10139. 「一本通 4.5 练习 1」树上操作(loj2125. 「HAOI2015」树上操作 )

loj2278. 「HAOI2017」字符串

HAOI2016找相同字符