BZOJ 4361 isn 容斥+dp+树状数组
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4361
题意概述:
给出一个长度为N的序列A(A1,A2...AN)。如果序列A不是非降的,你必须从中删去一个数,重复这一操作,直到A非降为止。求有多少种不同的操作方案,答案模10^9+7。
N<=2000.
分析:
首先手算一下样例确定一下题意,不同的方案实际上就是删除数字的位置的不同排列。
当手算答案的时候可以发现我们可以把答案按照最终序列的长度分类。看题目的样子可以搜索但是怎么都弄不出一个子问题来构造方程,答案又可以分类,于是考虑一下容斥。
首先我们求出f(x)表示长度为x的非降子序列的数量(可以dp搞定,用个树状数组优化一下),然后将其意义令g(x)表示得到长度为x的非降子序列的合法方案数。
我们观察一下这两个东西:
g(N) = f(N)
g(N-1) = f(N-1) - N*g(N) = f(N-1) - N*f(N)
g(N-2) = f(N-2) - (N-1)*g(N-1) - (N-1)*N*g(N) = f(N-2) - (N-1)*( g(N-1)+N*g(N) ) = f(N-2) - (N-1)*f(N-1)
......
相信聪明机智的你一定发现了什么(滑稽),容斥的时候只要令g(i) = f(i) - (i+1)*f(i+1)就可以辣!
容斥的关键还是在于分类啊
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int maxn=2005; 14 const int mo=1000000007; 15 16 int N,A[maxn],rank[maxn],f[maxn][maxn],g[maxn],J[maxn]; 17 struct Binary_Index_Tree{ 18 static const int msz=2005; 19 int c[msz],n; 20 Binary_Index_Tree(){ 21 memset(c,0,sizeof(c)); n=0; 22 } 23 void update(int i,int v){ 24 while(i<=n) c[i]=(c[i]+v)%mo,i+=i&(-i); 25 } 26 int query(int i){ 27 int re=0; 28 while(i) re=(re+c[i])%mo,i-=i&(-i); 29 return re; 30 } 31 }bit[maxn]; 32 33 bool nega; 34 void _scanf(int &x) 35 { 36 x=0,nega=0; 37 char ch=getchar(); 38 while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); 39 if(ch=='-') nega=1,ch=getchar(); 40 while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); 41 if(nega) x=-x; 42 } 43 void data_in() 44 { 45 _scanf(N); 46 for(int i=1;i<=N;i++) _scanf(A[i]); 47 } 48 void work() 49 { 50 memcpy(rank,A,sizeof(A)); 51 sort(rank+1,rank+N+1); 52 int cnt=unique(rank+1,rank+N+1)-rank-1; 53 for(int i=1;i<=N;i++) bit[i].n=cnt; 54 for(int i=1;i<=N;i++) 55 A[i]=lower_bound(rank+1,rank+cnt+1,A[i])-rank; 56 for(int i=1;i<=N;i++){ 57 f[i][1]=1; 58 for(int j=2;j<=i;j++) 59 f[i][j]=(f[i][j]+bit[j-1].query(A[i]))%mo; 60 for(int j=1;j<=i;j++) 61 bit[j].update(A[i],f[i][j]); 62 } 63 for(int i=1;i<=N;i++) 64 for(int j=1;j<=i;j++) g[j]=(g[j]+f[i][j])%mo; 65 J[0]=1; 66 for(int i=1;i<=N;i++) J[i]=1ll*i*J[i-1]%mo; 67 for(int i=1;i<=N;i++) g[i]=1ll*g[i]*J[N-i]%mo; 68 for(int i=1;i<N;i++) g[i]=(g[i]-1ll*g[i+1]*(i+1)%mo+mo)%mo; 69 int ans=0; 70 for(int i=1;i<=N;i++) ans=(ans+g[i])%mo; 71 printf("%d\n",ans); 72 } 73 int main() 74 { 75 data_in(); 76 work(); 77 return 0; 78 }