zoukankan      html  css  js  c++  java
  • [CF736D] Permutations

    可以发现如果我们只询问一次答案其实就是询问将其二分图的邻接矩阵建出来的行列式 $(det A)$ 的奇偶性。

    因为 $det(A)=sum_{sigmain S_n} sgn(sigma)prod a_{i,sigma(i)}$ ,可以发现我们对于 $sigma$ 来说只有均为 $1$ 才做贡献,且由于奇偶性我们不需要知道 $sgn(sigma)$ 。

    故我们只需要快速求将一个 $1$ 改成 $0$ 的行列式值为多少。

    根据
    $$
    det(A)=sum_{c=1}^n a_{r,c}A_{r,c}
    $$
    其中 $A_{r,c}$ 表示 $a_{r,c}$ 的代数余子式,$A_{i,j}=(-1)^{i+j} M_{i,j}$ ,$M_{i,j}$ 表示 $A$ 中删去第 $i$ 行第 $j$ 列的行列式。

    则我们将 $a_{r,c}=1$ 改成 $a_{r,c}=0$ 时我们只需要知道 $A_{r,c}$ 的奇偶性即可。

    由于
    $$
    sum_{c=1}^n a_{i,c}A_{j,c}=det(A)cdot [i=j]
    $$
    将其按照矩阵乘法写出即为
    $$
    A(A_{i,j})^T=det(A)cdot I_n
    $$
    其中 $A^*=(A_{i,j})^T $,$A^*$ 表示 $A$ 的伴随矩阵。

    其实我们将行/列展开的值是一样的,故 $AA^*=A^*A=det(A)cdot I_n$ 。

    由于我们只需要快速知道 $A_{r,c}$ 即可,根据上式可写
    $$
    A^*=det(A)cdot A^{-1}
    $$
    又由于 $det(A)$ 为奇数,则我们只需要求 $A^{-1}$ 做个矩阵求逆就好啦!

    求逆由于为 $0/1$ 异或故利用 $bitset$ 优化,时间复杂度 $O(dfrac{n^3}{w})$ 。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<climits>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<bitset>
    #define pii pair<int,int>
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define se second
    using namespace std;
    inline int read(){
        int f=1,ans=0; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    const int MAXN=2e3+11;
    const int MAXM=5e5+11;
    bitset<MAXN*2> A[MAXN];
    int N,M,U[MAXM],V[MAXM];
    void print(){printf("==========
    ");for(int i=1;i<=N;i++){for(int j=1;j<=2*N;j++) cout<<A[i][j]<<" ";printf("
    ");}printf("===========
    ");}
    void Gauss(){
        for(int i=1;i<=N;i++){
            int ps=0; for(int j=i;j<=N;j++) if(A[j][i]){ps=j;break;}
            swap(A[i],A[ps]);
            for(int j=1;j<=N;j++){
                if(!A[j][i]||i==j) continue;
                A[j]^=A[i];
            }
        }return;
    }
    int main(){
        //freopen("2.in","r",stdin);
        N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read();U[i]=u,V[i]=v;A[u][v]=1;}
        for(int i=1;i<=N;i++) A[i][i+N]=1; Gauss();
        for(int i=1;i<=M;i++) printf(A[V[i]][U[i]+N]?"NO
    ":"YES
    ");
        return 0;
    }
    View Code
  • 相关阅读:
    js---小火箭回到顶部
    JS小案例--简单时钟
    堆排序以及TopK大顶堆小顶堆求解方式(js版)
    svg-icon
    Vue 点击按钮 触发 input file 选择文件
    图片裁剪放大缩小旋转 Cropper.js
    Cytoscape
    vue d3 force cytoscape
    获取当月多少天
    谷歌打印去页脚
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/14282661.html
Copyright © 2011-2022 走看看