题目大意不说了
开始时感觉比赛三道题都不会做。
于是BFS式想题,
后来突然发现了一些不错的性质,一下子有了希望,认为自己能切。
可到最后才发现自己的核心部分还是没有想出来。
首先是一些简单的性质:
计算 K K K次前缀和后的和,就是 K + 1 K+1 K+1次前缀和之后最后一项的和。
计算前缀和的时候可以横着先计算若干次,竖着再计算若干次。
后面都考虑一维前缀和:
前缀和若干次之后每项的系数就不说了,打打表就能出来。(亏我比赛时用生成函数分析)
显然是一个线性的多项式。
假如我们把求 K K K次前缀和视为一种运算(就记作 s u m K ( A ) sum_K(A) sumK(A)把),那满足分配律 s u m K ( A + B ) = s u m K ( A ) + s u m K ( B ) sum_K(A+B)=sum_K(A)+sum_K(B) sumK(A+B)=sumK(A)+sumK(B)
这条东西是比较重要的。
对于每个行,我们都它看成一个整体。
行交换的时候,就是行的对应位置交换。
列交换的时候,就是每个行两个位置交换。
所有的权值都可以表示成 m i + j mi+j mi+j的形式,拆开来,分别计算 m i mi mi和 j j j的前缀和。对于 m i mi mi, m m m可以提出来,就是 i i i的前缀和。每行的 i i i都相同,对其中一行计算,出来的结果就是一个系数乘 i i i,这个系数在每一行都是一样的,提出来。接着再对列计算,就转成了求 i i i的一维前缀和, j j j类似。
所以我们只需要用个数据结构,分别维护行、列的一维前缀和。
比赛时打到最后突然发现自己不会维护,心态崩了……
现在的问题就是怎么维护这个前缀和了。
以行为例,某一行第 i i i列的系数是: C K + x 2 − i K = ( K + x 2 − i ) K ‾ C_{K+x2-i}^{K}=(K+x_2-i)^{\underline{K}} CK+x2−iK=(K+x2−i)K
这个东西叫下降幂。
下降幂有个公式: x n ‾ = ∑ i = 0 n ( − 1 ) n − i s n i x i x^{\underline{n}}=\sum_{i=0}^n(-1)^{n-i}s_n^ix^i xn=∑i=0n(−1)n−isnixi
s s s是第一类斯特林数。
至于怎么证明就百度去吧,用归纳法是可以证的,
用这个东西来推式子,最后会搞出这个东西:
∑ d = 0 K ( − 1 ) K − d s K d ∑ e = 0 d ( x 2 + k ) e ( − i ) d − e \sum_{d=0}^K(-1)^{K-d}s_K^d\sum_{e=0}^d(x_2+k)^e(-i)^{d-e} ∑d=0K(−1)K−dsKd∑e=0d(x2+k)e(−i)d−e
于是,维护 a i ∗ ( − i ) k a_i*(-i)^k ai∗(−i)k( k ∈ [ 0 , 10 ] k\in [0,10] k∈[0,10])的和就好了。
using namespace std;
#include
#include
#include
#include
#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;
}
这是一道披着数据结构的皮的计数题……
看来还是要学一些强大的计数公式。