考虑我们已经有区间([L, R]),设(f_i)为在进行若干次操作后第i个恶魔连续出现在末尾且前面没有连续出现的恶魔的方案数。
先不考虑前面有没有连续出现,再枚举前面可以连续出现的恶魔去除掉。
[f_i = frac{(sum_{jin[L, R]} p_{i, j}-1)!}{prod_{jin[L, R]}(p_{i,j}-1)!}cdot(R-L+1)! - sum_{jin S}f_jcdot frac{(sum_{xin[L, R]}p_{i,x}-p_{j,x}-1)!}{prod_{xin[L, R]}(p_{i,x}-p_{j,x}-1)!}cdot(R-L+1)!
]
其中(S)代表可以连续出现在i以前的恶魔编号集合,(p_{i,j})表示i恶魔在j井中的位置。
我们在每口井后面都增加一个n+1的恶魔,这样答案就是(frac {f_{n+1}} {(R-L+1)!})
朴素的算法是(O(n^2k^3))的,但是我们发现固定左端点,枚举右端点时,分式的分子与分母分开来容易维护。注意到随机数据,那么(S)每次期望缩小(frac 1 2),时间复杂度就变成了(O(nk(n+k)))
然而我还是要吸口氧才能过
#pragma GCC optimize("-O2")
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxk=300, maxn=301, mods=1000000007;
ll fact[maxk*maxn+1], invf[maxk*maxn+1];
ll qpower(ll a, int n) {
ll s=1;
for (; n; n/=2) {
if (n&1) s=s*a%mods;
a=a*a%mods;
}
return s;
}
void initFact(int n) {
fact[0] = 1;
for (int i=1; i<=n; i++) fact[i] = fact[i-1]*i%mods;
invf[n] = qpower(fact[n], mods-2);
for (int i=n; i>0; i--) invf[i-1] = invf[i]*i%mods;
}
int main() {
freopen("nothard.in", "r", stdin);
freopen("nothard.out", "w", stdout);
static int a[maxk+1][maxn+1], p[maxn+1][maxk+1];
int k, n;
scanf("%d %d", &k, &n);
initFact(n*k);
for (int i=1; i<=k; i++) {
for (int j=1; j<=n; j++) {
scanf("%d", a[i]+j);
p[i][a[i][j]] = j;
}
a[i][n+1] = p[i][n+1] = n+1;
}
n++;
static int cor[2][maxn+1][maxn+1];
static ll f[maxn+1], u[maxn+1][maxn+1], v[maxn+1][maxn+1];
ll ans=0, now=0;
for (int i=1; i<=k; i++) {
int pos=0;
for (int j=1; j<=n; j++) {
int o=a[i][j];
cor[pos][o][0] = j-1;
u[o][o]=j-1;
v[o][o]=invf[j-1];
for (int t=1; t<j; t++) {
cor[pos][o][t] = a[i][t];
u[o][a[i][t]] = j-t-1;
v[o][a[i][t]] = invf[j-t-1];
}
}
for (int j=i+1; j<=k; j++) {
static int book[maxn+1];
book[0]++;
pos=!pos;
for (int x=1; x<=n; x++) {
int o=a[j][x];
u[o][o] += x-1;
v[o][o] = v[o][o]*invf[x-1]%mods;
f[o] = fact[u[o][o]]*v[o][o]%mods*fact[j-i+1]%mods;
cor[pos][o][0] = 0;
for (int y=1; y<=cor[!pos][o][0]; y++) {
int z=cor[!pos][o][y];
if (book[z]==book[0]) {
cor[pos][o][++cor[pos][o][0]] = z;
int t=x-p[j][z]-1;
u[o][z] += t;
v[o][z] = v[o][z]*invf[t]%mods;
f[o] = (f[o]+mods-f[z]*fact[u[o][z]]%mods*v[o][z]%mods*fact[j-i+1]%mods)%mods;
}
}
book[o] = book[0];
}
ans = (ans+f[n]*invf[j-i+1]%mods)%mods;
}
}
printf("%lld
", ans);
fclose(stdin);
fclose(stdout);
return 0;
}