BZOJ4361 isn(动态规划+树状数组+容斥原理)
首先dp出长度为i的不下降子序列个数,显然这可以树状数组做到O(n2logn)。
考虑最后剩下的序列是什么,如果不管是否合法只是将序列删至只剩i个数,那么方案数显然是f[i]*(n-i)!。如果不合法,说明这个序列是由一个长度为i+1的非降序列删除一个数得来的,所以将其减去f[i+1]*(i+1)*(n-i-1)。这里的斥显然不会有重复。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 2010 #define P 1000000007 int n,a[N],b[N],f[N],tree[N][N],ans[N],fac[N],t,tot; inline int inc(int &x,int y){x+=y;if (x>=P) x-=P;} void add(int x,int k,int p){while (k<=t) inc(tree[x][k],p),k+=k&-k;} int query(int x,int k){int s=0;while (k) inc(s,tree[x][k]),k-=k&-k;return s;} int main() { #ifndef ONLINE_JUDGE freopen("bzoj4361.in","r",stdin); freopen("bzoj4361.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(); for (int i=1;i<=n;i++) b[i]=a[i]=read(); sort(b+1,b+n+1); t=unique(b+1,b+n+1)-b-1; for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+t+1,a[i])-b; f[0]=1;add(0,1,1); for (int i=1;i<=n;i++) { for (int j=1;j<=i;j++) inc(ans[j],f[j]=query(j-1,a[i])); for (int j=1;j<=i;j++) add(j,a[i],f[j]); } fac[0]=1; for (int i=1;i<=n;i++) fac[i]=1ll*i*fac[i-1]%P; for (int i=1;i<=n;i++) inc(tot,(1ll*ans[i]*fac[n-i]%P-1ll*ans[i+1]*fac[n-i-1]%P*(i+1)%P+P)%P); cout<<tot; return 0; }