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;
    }
  • 相关阅读:
    Java Web 网络留言板2 JDBC数据源 (连接池技术)
    Java Web 网络留言板3 CommonsDbUtils
    Java Web ConnectionPool (连接池技术)
    Java Web 网络留言板
    Java Web JDBC数据源
    Java Web CommonsUtils (数据库连接方法)
    Servlet 起源
    Hibernate EntityManager
    Hibernate Annotation (Hibernate 注解)
    wpf控件设计时支持(1)
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12003446.html
Copyright © 2011-2022 走看看