zoukankan      html  css  js  c++  java
  • 【UOJ453】【集训队作业2018】围绕着我们的圆环 线性基 DP

    题目大意

      有一个 (n imes k) 的 01矩阵 (C),求有多少个 (n imes m) 的矩阵 (A)(m imes k) 的矩阵 (B),满足 (A imes B=C)。系数对 (2) 取模。

      还有 (q) 次操作,每次会修改 (C) 中一行的值。

      要对每次修改后的矩阵计算答案。

      (n,m,k,qleq 1000)

    题解

      可以发现,答案只跟 (C) 的秩有关。因为如果我们对 (C) 做行变换或列变换,那么就可以对 (A)(B) 做同样的行变换或列变换,使得等式依然成立。

      记 (C) 的秩为 (r)

      记 (C_i) 表示 (C) 的列向量,(A_i) 表示 (A) 的列向量。

      我们先枚举矩阵 (A),对于每个 (C_i),它都是由若干个 (A_j) 异或得到的,系数为 (B_{j,i})

      只有所有 (C_i) 都在 (A_j) 生成的线性空间中时,才有合法的 (B)

      若 (A) 的秩为 (x),那么 (B) 方案数就有 (2^{k(m-x)}) 种。

      我们先对所有秩为 (r) 的矩阵统计方案数,再除以秩为 (r) 的矩阵个数即可。

      枚举 (A) 的秩 (x),那么合法的 (C) 的每个列向量都可以由 (A) 的列向量组合而成,可以写成一个 (k imes x) 的矩阵,且这个矩阵的秩为 (r)

      记 (f_{i,j}) 表示 (n imes i) 的秩为 (j) 的矩阵个数,(p_{i,j}) 表示 (k imes 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
    ",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
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    .net core 3.1 使用Redis缓存
    JavaSE 高级 第11节 缓冲输入输出字节流
    JavaSE 高级 第10节 字节数组输出流ByteArrayOutputStream
    JavaSE 高级 第09节 字节数组输入流ByteArrayInputStream
    JavaSE 高级 第08节 文件输出流FileOutputStream
    JavaSE 高级 第07节 文件输入流FileInputStream
    JavaSE 高级 第06节 初识I、O流
    JavaSE 高级 第05节 日期类与格式化
    JavaSE 高级 第04节 StringBuffer类
    JavaSE 高级 第03节 Math类与猜数字游戏
  • 原文地址:https://www.cnblogs.com/ywwyww/p/10194323.html
Copyright © 2011-2022 走看看