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^d\sum_{e=0}^d(x_2+k)^e(-i)^{d-e}\)
于是,维护\(a_i*(-i)^k\)\(k\in [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\n",(t+p)%mo);
        }
    }
    return 0;
}

总结

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

猜你喜欢

转载自www.cnblogs.com/jz-597/p/12300461.html
今日推荐