Bzoj4361 Isn
题意:
给出一个长度为n的序列A(A1,A2…AN)。如果序列A不是非降的,你必须从中删去一个数,
这一操作,直到A非降为止。求有多少种不同的操作方案,答案模10^9+7。
注意呀,是到非降即停,而不是求有多少种非降的序列
解题思路
可以发现非常类似CF597C的这道题,区别在于,CF这道题是求严格增与多少种序列
我们可以把Bzoj这题往CF上靠,只要排序之后,然后记录编号,再重新赋值,根据顺序对位置为编号重新赋值,既实现了离散化,又转换了问题,求变成严格增的方案
for (int i = 1; i <= n; i++) {
cin >> p[i].num;
p[i].ord = i;
}
sort (p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i++) {
g[p[i].ord] = i;
}
建立以长度为第一维的树状数组,利用迭代,记录每个长度以当前位置为结尾的严格增的个数(先不考虑怎样取数)
顺便利用个二维数组来记录下当前长度的位置有几个
for (int i = 1; i <= n; i++) {
a[1][i] = 1;
update(1, g[i], 1);
for (int k = 2; k <= n ; k++) {
ll tmp = getsum(k - 1, g[i] - 1) % mod;
a[k][i] = tmp % mod; //长度为k的以i位置结尾的严格增序列个数
update(k, g[i], tmp % mod);
}
}
接下来是队友告诉我的思路:(容斥)
fac[]为阶乘函数,num[]为当前长度有几个
对于当前l长度可以先统计==所有可能到达的种类数p = fac[n-l] * num[l] ==
显然种类p不一定是正确答案,因为可能是由一个长度为l+1的合法序列删除一个得来的(有l+1种方法),所以减去fac[n-l+1] * num[l + 1] * (l + 1)
在减去的方案中,无论这是不是合法的方案,都会在之前就已经被计入ans中,所以这样做是对的,而合法的方案,之后又会重新加上
AC代码
#include <bits/stdc++.h>
#define lowbit(x) (x & (-x))
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
typedef long long ll;
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 2e3 + 10;
ll n, ans = 0;
ll g[maxn], fac[maxn];
ll c[maxn][maxn];
ll a[maxn][maxn];
struct P {
ll num, ord;
} p[maxn];
void cal() {
fac[0] = 1;
for (ll i = 1; i <= 2000; i++) fac[i] = fac[i - 1] % mod * i % mod;
}
bool cmp (P A, P B) {
if (A.num != B.num) return A.num < B.num;
else return A.ord < B.ord;
}
void update(ll m, ll i, ll k) {
while (i < maxn) c[m][i] = (c[m][i] + k) % mod, i += lowbit(i);
}
ll getsum(ll m, ll i) {
ll res = 0;
while (i) res = (res + c[m][i]) % mod, i -= lowbit(i);
return res % mod;
}
int main() {
IOS;
cal();
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i].num;
p[i].ord = i;
}
sort (p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i++) {
g[p[i].ord] = i;
}
for (int i = 1; i <= n; i++) {
a[1][i] = 1;
update(1, g[i], 1);
for (int k = 2; k <= n ; k++) {
ll tmp = getsum(k - 1, g[i] - 1) % mod;
a[k][i] = tmp % mod;
update(k, g[i], tmp % mod);
}
}
ll pnum = 0, nnum = 0;
for (int j = 1; j <= n; j++) pnum += a[1][j];
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= n; j++)
nnum += a[i][j];
nnum %= mod, pnum %= mod;//pnum计算前一行,nnum计算当一行
ans = ((ans + pnum % mod * fac[n - i + 1] % mod - i * nnum % mod * fac[n - i] % mod) % mod + mod) % mod;
pnum = nnum, nnum = 0;
}
cout << ans << endl;
}