• 【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;
    }
    
  • 相关阅读:
    使用UFW防火墙拦截特定IP地址
    利用iptables防止syn flood攻击
    DDOS攻击模拟复现
    服务器遭到SYN攻击怎么办?如何防御SYN攻击?
    iptables限制同一IP连接数
    TIME_WAIT和CLOSE_WAIT状态区别
    详解TIME_WAIT过多,引起的请求502的问题。
    TIME_WAIT太多解决思路和方法
    黑客是如何攻击 WebSockets 和 Socket.io的
    免费CDN加速CloudFlare申请教程及使用 提供免费加速和防DDOS攻击
  • 原文地址:https://www.cnblogs.com/ywwyww/p/10194323.html
Copyright © 2020-2023  润新知