题目
题目大意不说了
思考历程
开始时感觉比赛三道题都不会做。
于是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 <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;
}
总结
这是一道披着数据结构的皮的计数题……
看来还是要学一些强大的计数公式。