牛客网多校训练第一场 E - Removal(线性DP + 重复处理)
链接:
https://www.nowcoder.com/acm/contest/139/E
题意:
给出一个n(1≤n≤1e5)个整数(范围是1至10)的序列,
求从中移除m(1≤m≤min(n-1,10))个整数后不同序列的数量模(1e9+7)。
分析:
设d[i][t]表示当前匹配到了第i个数字,总共删了t个数字时的不同序列的数量。
先不考虑序列重复的情况,
则d[i][t] = d[i-1][t](不删第i个数字)+ d[i-1][t-1](删第i个数字)。
现在考虑减去重复的序列。
设有序列abcdec(以字符串为例),可以发现,s[3]=s[6],当i=6,t=3时,
删除cde与删除dec得到的序列是一样的,都是abc,即匹配到s[6]时产生了重复。
这时减去删除cde的方案数(与其前面序列ab相应的方案数相同)即可。
即d[6][3]减去d[2][0](表示从前面的2个元素中删除0个元素,最后再删除cde的方案数)。
代码:
1 import java.io.*; 2 import java.util.*; 3 import static java.util.Arrays.*; 4 5 public class Main { 6 Scanner cin = new Scanner(new BufferedInputStream(System.in)); 7 final int UP = (int)1e5 + 5; 8 final long MOD = (long)1e9 + 7; 9 int last[] = new int[10+5]; // last[i]:数字i之前出现的最后位置 10 long d[][] = new long[UP][10+5]; 11 12 void MAIN() { 13 for(int i = 0; i < 10+5; i++) d[i][i] = 1; 14 for(int i = 0; i < UP; i++) d[i][0] = 1; 15 while(cin.hasNext()) { 16 int n = cin.nextInt(); 17 int m = cin.nextInt(); 18 cin.nextInt(); 19 fill(last, 0); 20 for(int v, i = 1; i <= n; i++) { 21 v = cin.nextInt(); 22 for(int t = 1; t <= m; t++) { 23 d[i][t] = (d[i-1][t] + d[i-1][t-1]) % MOD; 24 if(i - last[v] > t || last[v] == 0) continue; // 当i-last[v]<=t时才会出现重复 25 d[i][t] = (d[i][t] - d[last[v]-1][t-(i-last[v])] + MOD) % MOD; 26 } 27 last[v] = i; 28 } 29 System.out.println(d[n][m]); 30 } 31 } 32 33 public static void main(String args[]) { new Main().MAIN(); } 34 }