zoukankan      html  css  js  c++  java
  • 6466. 【GDOI2020模拟02.09】​矩阵求和

    题目

    题目大意不说了


    思考历程

    开始时感觉比赛三道题都不会做。
    于是BFS式想题,
    后来突然发现了一些不错的性质,一下子有了希望,认为自己能切。
    可到最后才发现自己的核心部分还是没有想出来。

    首先是一些简单的性质:
    计算(K)次前缀和后的和,就是(K+1)次前缀和之后最后一项的和。
    计算前缀和的时候可以横着先计算若干次,竖着再计算若干次。
    后面都考虑一维前缀和:
    前缀和若干次之后每项的系数就不说了,打打表就能出来。(亏我比赛时用生成函数分析)
    显然是一个线性的多项式。
    假如我们把求(K)次前缀和视为一种运算(就记作(sum_K(A))把),那满足分配律(sum_K(A+B)=sum_K(A)+sum_K(B))
    这条东西是比较重要的。

    对于每个行,我们都它看成一个整体。
    行交换的时候,就是行的对应位置交换。
    列交换的时候,就是每个行两个位置交换。
    所有的权值都可以表示成(mi+j)的形式,拆开来,分别计算(mi)(j)的前缀和。对于(mi)(m)可以提出来,就是(i)的前缀和。每行的(i)都相同,对其中一行计算,出来的结果就是一个系数乘(i),这个系数在每一行都是一样的,提出来。接着再对列计算,就转成了求(i)的一维前缀和,(j)类似。
    所以我们只需要用个数据结构,分别维护行、列的一维前缀和。

    比赛时打到最后突然发现自己不会维护,心态崩了……


    正解

    现在的问题就是怎么维护这个前缀和了。
    以行为例,某一行第(i)列的系数是:(C_{K+x2-i}^{K}=(K+x_2-i)^{underline{K}})
    这个东西叫下降幂。
    下降幂有个公式:(x^{underline{n}}=sum_{i=0}^n(-1)^{n-i}s_n^ix^i)
    (s)是第一类斯特林数。
    至于怎么证明就百度去吧,用归纳法是可以证的,

    用这个东西来推式子,最后会搞出这个东西:
    (sum_{d=0}^K(-1)^{K-d}s_K^dsum_{e=0}^d(x_2+k)^e(-i)^{d-e})
    于是,维护(a_i*(-i)^k)(kin [0,10]))的和就好了。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    #define N 100100
    #define ll long long
    #define mo 1000000007
    ll qpow(ll x,int y=mo-2){
    	ll res=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			res=res*x%mo;
    	return res;
    }
    int n,m,Q;
    ll s[11][11];
    int R[N],C[N];
    ll pow_ik[N][11];
    ll fac[N],ifac[N];
    inline ll Cmn(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
    struct Info{
    	ll s[11];
    } d[2][N*4];
    inline void upd(Info &c,Info &a,Info &b){
    	for (int k=0;k<=10;++k)
    		c.s[k]=(a.s[k]+b.s[k])%mo;
    }
    void init(int num,int k,int l,int r){
    	if (l==r){
    		for (int i=0;i<=10;++i)
    			d[num][k].s[i]=pow_ik[l][i]*(num==0?C[l]:R[l])%mo;
    		return;
    	}
    	int mid=l+r>>1;
    	init(num,k<<1,l,mid);
    	init(num,k<<1|1,mid+1,r);
    	upd(d[num][k],d[num][k<<1],d[num][k<<1|1]);
    }
    void change(int num,int k,int l,int r,int x){
    	if (l==r){
    		for (int i=0;i<=10;++i)
    			d[num][k].s[i]=pow_ik[l][i]*(num==0?C[l]:R[l])%mo;
    		return;
    	}
    	int mid=l+r>>1;
    	if (x<=mid)
    		change(num,k<<1,l,mid,x);
    	else
    		change(num,k<<1|1,mid+1,r,x);
    	upd(d[num][k],d[num][k<<1],d[num][k<<1|1]);
    }
    Info res;
    void query(int num,int k,int l,int r,int st,int en){
    	if (st<=l && r<=en){
    		for (int i=0;i<=10;++i)
    			(res.s[i]+=d[num][k].s[i])%=mo;
    		return;
    	}
    	int mid=l+r>>1;
    	if (st<=mid)
    		query(num,k<<1,l,mid,st,en);
    	if (mid<en)
    		query(num,k<<1|1,mid+1,r,st,en);
    }
    inline ll qu(int num,int l,int r,int K){ 
    	memset(&res,0,sizeof res);
    	query(num,1,1,num==0?m:n,l,r);
    	K--;
    	ll ans=0;
    	for (ll d=K,_1=1;d>=0;--d,_1*=-1){
    		ll sum=0;
    		for (ll e=0,x=1;e<=d;++e){
    			sum+=Cmn(d,e)*x%mo*res.s[d-e]%mo;
    			x=x*(K+r)%mo;
    		}
    		sum=sum%mo*s[K][d]%mo;
    		ans=(ans+sum*_1+mo)%mo;
    	}
    	return ans*ifac[K]%mo;
    }
    int main(){
    	freopen("matrix.in","r",stdin);
    	freopen("matrix.out","w",stdout);
    //	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	s[0][0]=1;
    	for (int i=1;i<=10;++i){
    		s[i][0]=0;
    		for (int j=1;j<=i;++j)
    			s[i][j]=(s[i-1][j-1]+s[i-1][j]*(i-1))%mo;
    	}
    	scanf("%d%d%d",&n,&m,&Q);
    	int nm=max(n,m);
    	for (int i=1;i<=nm;++i){
    		pow_ik[i][0]=1;
    		for (int j=1;j<=10;++j)
    			pow_ik[i][j]=pow_ik[i][j-1]*(mo-i)%mo;
    	}
    	nm+=11;
    	fac[0]=1;
    	for (int i=1;i<=nm;++i)
    		fac[i]=fac[i-1]*i%mo;
    	ifac[nm]=qpow(fac[nm]);
    	for (int i=nm-1;i>=0;--i)
    		ifac[i]=ifac[i+1]*(i+1)%mo;
    	for (int i=1;i<=n;++i)	
    		R[i]=i-1;
    	for (int j=1;j<=m;++j)
    		C[j]=j;
    	init(0,1,1,m);
    	init(1,1,1,n);
    	while (Q--){
    		char ch[2];
    		scanf("%s",ch);
    		if (ch[0]=='R'){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			swap(R[x],R[y]);
    			change(1,1,1,n,x);
    			change(1,1,1,n,y);		
    		}
    		else if (ch[0]=='C'){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			swap(C[x],C[y]);
    			change(0,1,1,m,x);
    			change(0,1,1,m,y);
    		}
    		else{
    			int x1,y1,x2,y2,K;
    			scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&K),K++;
    			ll t=qu(0,y1,y2,K)*Cmn(x2-x1+1+K-1,K)%mo;
    			ll p=qu(1,x1,x2,K)*Cmn(y2-y1+1+K-1,K)%mo*m%mo;
    			printf("%lld
    ",(t+p)%mo);
    		}
    	}
    	return 0;
    }
    

    总结

    这是一道披着数据结构的皮的计数题……
    看来还是要学一些强大的计数公式。

  • 相关阅读:
    测试开发进阶——Spring cloud——理解——微服务中微服务网关理解(转载)
    测试开发进阶——Spring cloud——理解——微服务中断路器模式理解(转载)
    测试开发进阶——Spring cloud——理解——微服务中负载均衡理解(转载)
    ORM 多表作业
    python-Matplotlib库
    beego Dockerfile
    java单例模式-饿汉式
    Java字符串连接:Java8字符串连接收集器Collectors.joining
    Odoo14学习笔记(11) 实例-简单的按年份月份查询报表
    002. git 分支管理
  • 原文地址:https://www.cnblogs.com/jz-597/p/12300461.html
Copyright © 2011-2022 走看看