Removal题解
Removal
https://ac.nowcoder.com/acm/problem/17137
题意:N个数,删除M个数字,所有的数字都是小于K。
问你最后有几种子序列。
题解:首先数据范围一看可以DP,然后我们就要想表达式。
如下:DP[i][j]=dp[i-1][j-1]+dp[i-1][j];
翻译:当我加入第i个数的是时候,我删除j个数。等于符号后面的是由上一次的继承下来,首先我本次的第i个数删除,那么就还有j-1个数删除,也就是第i个数删了,之后是第i个数不删,删除前面的j个。
这样一来我们是不是就是把所有的子序列都搞出来,但是我们在这个过程中会有重复的计算,列如,1 2 3 2 4 3,当我们i=6,j=4,那么是不是删除324,和删除243会得到相同的子序列,这时候就是重复了,那么如何处理呢,首先我们每次把每个数都记录下来,判断这个数字在上次的那个位置出现,那么是不是就有这个k(上次出现此数的位置),使用上面的i,j,我们要想知道是否删除数字会不会重复,是不是应该 判断i-k和j的大,如果小于说明k-i的中间删除了都还有数字可以删除,对吧,这时候就有重复的了,dp[6][4]=dp[5][4]+dp[5][3]这是总的个数,之后我们如何减去重复的呢,是不是就应该回到上一个重复的数那里,i-k=6-3=3,中间加上第i个一共有三个数,是不是我们还要删除一个,怎么办,是不是就是删除K前面的,这个时候还有一个数需要删除,我们就是dp[3][1],对吧,为什么我们不能直接dp[6][4]-dp[3][1]呢,想想,dp[3][1]=dp[2][1]+dp[2][0],代表我前面的3是不删和删,然后dp[6][4]=dp[5][4]+dp[5][3],代表后面的3是不删还是删,这样一看我dp[6][4],是由前面的继承下来的 那么是不是应该减去dp[2][1];然后最后注意mod的不要溢出。
#include <bits/stdc++.h> #define fio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define debug(x) cout << #x << ": " << x << endl; #define debug1(x) cout<<"xxx"<<endl; #define ll long long #define ull unsigned long long #pragma GCC optimize("Ofast","inline","-ffast-math") #pragma GCC target("avx,sse2,sse3,sse4,mmx") #define mse(a,b) memset(a,b,sizeof a); using namespace std; const int maxx=1e6+100; const int mod=1e9+7; int ans[maxx]; int last[maxx]; int p[maxx]; int dp[maxx][15]; int main() { fio; int n,m,k; while(cin>>n>>m>>k) { mse(last,0); mse(p,0); for(int i=1; i<=n; i++) { cin>>ans[i]; p[i]=last[ans[i]]; last[ans[i]]=i; } for(int i=0; i<=n; i++) { dp[i][i]=1; } for(int i=1; i<=n; i++) { dp[i][0]=1; for(int j=1; j<=min(i-1,m); j++) { dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;///删除第i个数+不会删除第i个数 if(p[i]!=0&&i-p[i]<=j) { dp[i][j]=(dp[i][j]-dp[p[i]-1][j+p[i]-i]+mod)%mod; } } cout<<dp[n][m]<<endl; } return 0; }