【UOJ453】【集訓隊做業2018】圍繞着咱們的圓環 線性基 DP

題目大意

  有一個 \(n\times k\) 的 01矩陣 \(C\),求有多少個 \(n\times m\) 的矩陣 \(A\)\(m\times k\) 的矩陣 \(B\),知足 \(A\times B=C\)。係數對 \(2\) 取模。dom

  還有 \(q\) 次操做,每次會修改 \(C\) 中一行的值。spa

  要對每次修改後的矩陣計算答案。code

  \(n,m,k,q\leq 1000\)get

題解

  能夠發現,答案只跟 \(C\) 的秩有關。由於若是咱們對 \(C\) 作行變換或列變換,那麼就能夠對 \(A\)\(B\) 作一樣的行變換或列變換,使得等式依然成立。string

  記 \(C\) 的秩爲 \(r\)it

  記 \(C_i\) 表示 \(C\) 的列向量,\(A_i\) 表示 \(A\) 的列向量。io

  咱們先枚舉矩陣 \(A\),對於每一個 \(C_i\),它都是由若干個 \(A_j\) 異或獲得的,係數爲 \(B_{j,i}\)function

  只有全部 \(C_i\) 都在 \(A_j\) 生成的線性空間中時,纔有合法的 \(B\)class

  若 \(A\) 的秩爲 \(x\),那麼 \(B\) 方案數就有 \(2^{k(m-x)}\) 種。im

  咱們先對全部秩爲 \(r\) 的矩陣統計方案數,再除以秩爲 \(r\) 的矩陣個數便可。

  枚舉 \(A\) 的秩 \(x\),那麼合法的 \(C\) 的每一個列向量均可以由 \(A\) 的列向量組合而成,能夠寫成一個 \(k\times x\) 的矩陣,且這個矩陣的秩爲 \(r\)

  記 \(f_{i,j}\) 表示 \(n\times i\) 的秩爲 \(j\) 的矩陣個數,\(p_{i,j}\) 表示 \(k\times i\) 的秩爲 \(j\) 的矩陣個數,那麼對答案的貢獻就是 \(f_{m,x}g_{x,r}2^{k(m-x)}\)

  最後把答案除以 \(f_{k,r}\) 便可。

  先預處理出 \(f,g\),就能夠在 \(O(n)\) 內回答一次詢問。

  如今咱們還要求 \(C\) 的秩 \(r\)

  對於線性基中的每一個向量和全部 \(0\) 向量維護這個向量是由哪些向量異或獲得的。

  在刪除一個向量 \(x\) 時,找到一個包含 \(x\)\(0\) 向量,若是沒有就找線性基裏位最低的包含 \(x\) 的向量,把這個向量的信息異或到其餘包含 \(x\) 的向量的信息中便可。這樣在刪除時不會影響線性基中更高位的向量。

  時間複雜度:\(O(\frac{(n+q)n^2}{w})\)

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
#include<bitset>
using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const ll p=1000000007;
const int N=1010;
ll fp(ll a,ll b)
{
    ll s=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1)
            s=s*a%p;
    return s;
}
typedef bitset<N> orzzjt;
typedef pair<orzzjt,orzzjt> zjtakioi2019;
ll pw[N*N];
ll f[N][N],g[N][N];
void add(ll &a,ll b)
{
    a=(a+b)%p;
}
int n,m,k;
ll solve(int x)
{
    ll res=0;
    for(int i=x;i<=n&&i<=m;i++)
        res=(res+f[m][i]*g[i][x]%p*pw[k*(m-i)])%p;
    res=res*fp(f[m][x],p-2)%p;
    res=(res%p+p)%p;
    return res;
}
ll e[N];
void init()
{
    pw[0]=1;
    for(int i=1;i<=1000000;i++)
        pw[i]=pw[i-1]*2%p;
    f[0][0]=1;
    for(int i=0;i<1000;i++)
        for(int j=0;j<=i&&j<=n;j++)
            if(f[i][j])
            {
                add(f[i+1][j],f[i][j]*pw[j]);
                add(f[i+1][j+1],f[i][j]*(pw[n]-pw[j]));
            }
    g[0][0]=1;
    for(int i=0;i<1000;i++)
        for(int j=0;j<=i&&j<=k;j++)
            if(g[i][j])
            {
                add(g[i+1][j],g[i][j]*pw[j]);
                add(g[i+1][j+1],g[i][j]*(pw[k]-pw[j]));
            }
    for(int i=0;i<=n&&i<=k;i++)
        e[i]=solve(i);
}
zjtakioi2019 a[N];
orzzjt b[N];
int t;
int r;
void insert(orzzjt x,int v)
{
    orzzjt y;
    y.set(v);
    for(int i=1000;i>=1;i--)
        if(x[i])
        {
            if(!a[i].first.any())
            {
                a[i].first=x;
                a[i].second=y;
                r++;
                return;
            }
            x^=a[i].first;
            y^=a[i].second;
        }
    t++;
    b[t]=y;
}
void erase(int x)
{
    for(int i=1;i<=t;i++)
        if(b[i][x])
        {
            swap(b[i],b[t]);
            t--;
            for(int j=1;j<=1000;j++)
                if(a[j].second[x])
                    a[j].second^=b[t+1];
            for(int j=1;j<=t;j++)
                if(b[j][x])
                    b[j]^=b[t+1];
            return;
        }
    for(int i=1;i<=1000;i++)
        if(a[i].second[x])
        {
            for(int j=i+1;j<=1000;j++)
                if(a[j].second[x])
                {
                    a[j].first^=a[i].first;
                    a[j].second^=a[i].second;
                }
            a[i].first=a[i].second=orzzjt();
            r--;
            return;
        }
}
int main()
{
    open("c");
    int q,type;
    scanf("%d%d%d%d%d",&n,&m,&k,&q,&type);
    init();
    int x,y;
    for(int i=1;i<=n;i++)
    {
        orzzjt s;
        for(int j=1;j<=k;j++)
        {
            scanf("%d",&x);
            if(x)
                s.set(j);
        }
        insert(s,i);
    }
    ll ans=e[r];
    printf("%lld\n",ans);
    for(int i=1;i<=q;i++)
    {
        scanf("%d",&x);
        x^=type*ans;
        erase(x);
        orzzjt s;
        for(int j=1;j<=k;j++)
        {
            scanf("%d",&y);
            if(y)
                s.set(j);
        }
        insert(s,x);
        ans=e[r];
        printf("%lld\n",ans);
    }
    return 0;
}
相關文章
相關標籤/搜索