zoukankan      html  css  js  c++  java
  • 毛皮立方体

    题目描述

    有个 $n imes n$ 的矩阵,要填入小于 $m$ 的自然数,有 $k$ 个格子已经填好了,要求横竖的和在模 $m$ 意义下都相等的方案数。

    数据范围

    $n,m le 10^9;k le 10^6$

    题解

    设有 $2n$ 个点分别表示行列,如果填入一个数的话就直接在点上加权,剩下空白的每个格子可以把它相对应的行列连接,形成二分图。考虑枚举最终每个行列的和,对于其中一个联通块,如果这个联通块是棵树,那它就是 $0/1$ 个解,如果是普通的联通块,考虑它的一个生成树,如果它有解,那剩下的边就可以乱填,因为加入一条边可以用树边来维持它合法。

    考虑哪些解 $x$ 会让其合法,假设一个联通块有 $a$ 行 $b$ 列,这 $a$ 行和 $n-b$ 列的权值总和为 $A$ ,这 $b$ 列和 $n-a$ 行的权值总和为 $B$ ,那么 $(a-b) imes x = A-B (mod m)$ ,这样就可以得到若干个同余方程,合并即可。

    考虑怎么求出这些联通块及其信息,发现 $k$ 不大,即可以求补-二分图的联通块,用链表维护即可。

    效率: $O(k)$

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int P=1e9+7,N=1e6+5;
    int n,k,m,f[2][N],a[N*2],b[N*2],t,ans=1,L[2][N],R[2][N];
    bool is[2][N],vs[N];
    struct O{int u,v,w;}p[N];
    struct Q{int o,x;};
    queue<Q>q,h;vector<Q>e[2][N];
    void add(int o,int u,int v,int w){
        e[o][u].push_back((Q){v,w});
    }
    int K(int x,int y){
        int z=1;
        for (;y;y>>=1,x=1ll*x*x%P)
            if (y&1) z=1ll*z*x%P;
        return z;
    }
    void del(int o,int x){
        L[o][R[o][x]]=L[o][x];R[o][L[o][x]]=R[o][x];
    }
    void bfs(int o,int x){
        del(o,x);q.push((Q){o,x});
        int z,w[2]={0,0},c[2]={0,0},v=0;Q u;
        while(!q.empty()){
            u=q.front();q.pop();c[u.o]++;
            (w[u.o]+=f[u.o][u.x])%=m;
            z=e[u.o][u.x].size();h.push(u);
            for (int i=0;i<z;i++)
                vs[e[u.o][u.x][i].o]=1;is[u.o][u.x]=1;
            for (int i=R[!u.o][0];i<=n;i=R[!u.o][i])
                if (!vs[i]) del(!u.o,i),q.push((Q){!u.o,i});
            for (int i=0;i<z;i++)
                vs[e[u.o][u.x][i].o]=0;
        }
        while(!h.empty()){
            u=h.front();h.pop();
            z=e[u.o][u.x].size();q.push(u);
            for (int i=0;i<z;i++)
                if (is[!u.o][e[u.o][u.x][i].o])
                    (w[u.o]+=m-e[u.o][u.x][i].x)%=m,v--;
        }
        v=(1ll*c[0]*c[1]+(v/2)-(c[0]+c[1]-1))%(P-1);
        while(!q.empty())
            u=q.front(),q.pop(),is[u.o][u.x]=0;
        a[++t]=(c[0]-c[1]+m)%m;b[t]=(w[0]-w[1]+m)%m;
        ans=1ll*ans*K(m,v)%P;
    }
    int exgcd(int A,int B,int &x,int &y){
        if (!B){x=1;y=0;return A;}
        int v=exgcd(B,A%B,y,x);y-=A/B*x;return v;
    }
    int main(){
        cin>>n>>k>>m;
        for (int i=1;i<=k;i++)
            scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].w);
        if (n>k) return printf("%d
    ",
            K(m,(1ll*n*(n-2)-k+2)%(P-1))),0;
        for (int i=1;i<=k;i++)
            add(0,p[i].u,p[i].v,p[i].w),
            (f[0][p[i].u]+=p[i].w)%=m,
            add(1,p[i].v,p[i].u,p[i].w),
            (f[1][p[i].v]+=p[i].w)%=m;
        for (int i=1;i<=n;i++)
            L[0][i]=L[1][i]=i-1,R[0][i]=R[1][i]=i+1;
        R[0][0]=R[1][0]=1;L[0][n+1]=L[1][n+1]=n;
        for (;R[0][0]<=n;bfs(0,R[0][0]));
        for (;R[1][0]<=n;bfs(1,R[1][0]));
        for (int x,y,z,i=1;i<=t;i++){
            if (a[i]){
                z=exgcd(a[i],m,x,y);
                x=(x%(m/z)+m/z)%(m/z);
                if (b[i]%z) return puts("0"),0;
                y=b[i]/z;b[i]=m/z;a[i]=1ll*x*y%b[i];
            }
            else{
                if (b[i]) return puts("0"),0;
                a[i]=0;b[i]=1;
            }
        }
        for (int v,x,y,z,i=2;i<=t;i++){
            z=exgcd(b[1],b[i],x,y);
            x=(x%(b[i]/z)+(b[i]/z))%(b[i]/z);v=a[i]-a[1];
            if (v%z) return puts("0"),0;
            b[1]=b[1]/z*b[i];
            a[1]=((a[1]+1ll*b[1]/b[i]*v%b[1]*x%b[1])%b[1]+b[1])%b[1];
        }
        if (m<=a[1]) return puts("0"),0;
        ans=1ll*ans*((m-a[1]-1)/b[1]+1)%P;
        printf("%d
    ",ans);return 0;
    }
  • 相关阅读:
    PHPStorm 支持 Element UI 语法提示
    npm
    谷歌浏览器插件
    RBAC
    git 知识点
    Laradock ppa加速
    vscode插件
    临时解决执行 Composer Install 返回 Killed 的问题
    单例设计模式(3种实现方式)
    log4j.properties 详解与配置步骤
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12003446.html
Copyright © 2011-2022 走看看