/*
如果 [l, r] 满足m个条件,那么[l, r+1]也满足条件
考虑尺取(双指针)
区间共长n,对于每一个i,我们可以求出最小的j,令其满足
满足m个条件、且从i开始,在j结束的最短子区间,设其长度为len
那么len个人从i开始坐,坐到j刚好满足,
如果有len+1个人,从i开始坐到j+1, 也满足
所以len个人、len+1个人坐法的方案可以分别+1
当且仅当 len <= (n-i+1) 是合法的(你总不超出n了吧,且左端点确定了)
我们可以注意到,我们要将len, len+1, len+2.....max(len) 都加上1,
即区间加、求单点
考虑差分
我们用suf维护长度为i,满足m个条件的合法方案数
最后输出的时候累加求和就是单点
对于x个人,要乘上x的阶乘,表示x个人全排列
注意取模和longlong问题即可
*/
#include <iostream>
#include <vector>
#define int long long
using namespace std;
const int N = 1e5 + 12, mod = 1e9 + 7;
int n, m;
vector<int> h[N];
int cnt[N], suf[N], fac[N*2];
/* suf 表示在满足m个的要求下,
从总长是n的区间里,最多能挑出多少个
连续的子区间 的差分数组
cnt 表示编号为i的座椅满足了多少个条件
当且仅当,cnt[i] 从0到1、或者从1到0的时候,需要对now加或减
因为编号是i,可能在同一个条件里出现两次或以上
*/
void add(int id, int &now)
{ // 把右指针移动到id,now现在会怎么变
for (auto u:h[id])
if ( ++ cnt[u] == 1)
now ++ ;
}
void del(int id, int &now)
{ // 删掉id,现在能满足多少个条件
for (auto u:h[id])
if (-- cnt[u] == 0)
now -- ;
}
signed main()
{
cin.tie(0) -> sync_with_stdio(0);
cin >> n >> m; // 区间长n, m个条件
for (int k=1;k <= 3;k ++ )
{
int u;
for (int i=1;i <= m;i ++ )
cin >> u, h[u].push_back(i);
// 编号id, 影响了那几个条件
}
int now = 0;
for (int i=1, j=1;i <= n;i ++ )
{ // 找到满足 now == m 的最近右端点
while (now < m && j <= n ) add(j ++, now);
if (now == m)
{
suf[(j-1)-i+1] ++ ; //
suf[(n-i+1)+1] -- ; // +1 是差分的用法
}
del(i, now);
}
fac[0] = fac[1] = 1;
for (int i=2;i <= n;i ++ )
fac[i] = (fac[i-1] * i ) % mod;
for (int i=1;i <= n;i ++ )
{
suf[i] += suf[i-1]; // 还原
// 还要乘上阶乘,表示i个人全排列
cout << suf[i]*fac[i]%mod << ' ';
}
return 0;
}