题意:
给定一个长度为 n n n的序列和整数 k k k,每个序列颜色为 a [ i ] a[i] a[i],找出一个子序列的长度为 k k k的倍数,假设为 l e n len len,则将这个子序列分为 l e n / k len/k len/k份,每份的颜色都相同。求长度最长的这样的子序列的个数。
解法1:
定义 
     
      
       
       
         d 
        
       
         p 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         = 
        
       
         { 
        
       
         以 
        
       
         i 
        
       
         颜色结尾的最大长度,数量 
        
       
         } 
        
       
      
        dp[i]=\{ 以i颜色结尾的最大长度,数量\} 
       
      
    dp[i]={以i颜色结尾的最大长度,数量}
 转移的话也较容易转移。
signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	init();
	int T;
	cin >> T;
	while(T--) {
		int n, k; cin >> n >> k;
		vector<int> a(n + 1);
		for(int i = 1; i <= n; i++) cin >> a[i];
		vector<vector<int>> sum(n + 1, vector<int> (n + 1));
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= n; j++) {
				sum[i][j] = sum[i][j - 1] + (a[j] == i);
			}
		}
		vector<pair<int, mint>> dp(n + 1);
		dp[0] = {0, 1};
		int mx = 0;
		for(int i = 1; i <= n; i++) {
			for(int j = 0; j < i; j++) {
				int cnt = sum[a[i]][i] - sum[a[i]][j];
				if(cnt >= k) {
					if(dp[j].fi + k > dp[i].fi) {
						dp[i] = {dp[j].fi + k, dp[j].se * C(cnt - 1, k - 1)};
					} else if(dp[j].fi + k == dp[i].fi) {
						dp[i].se += dp[j].se * C(cnt - 1, k - 1);
					}
					mx = max(mx, dp[i].fi);
				}
			}
		}
		mint ans;
		for(int i = 0; i <= n; i++) 
			if(dp[i].fi == mx) ans += dp[i].se;
		cout << ans << '\n';
	}
 
    return 0;
}
 
解法2:
定义 
     
      
       
       
         d 
        
       
         p 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         [ 
        
       
         j 
        
       
         ] 
        
       
      
        dp[i][j] 
       
      
    dp[i][j]表示选了 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i个数,最后一个颜色为 
     
      
       
       
         j 
        
       
      
        j 
       
      
    j的方案数。
 转移的话需要注意的是如果 
     
      
       
       
         i 
        
       
         % 
        
       
         k 
        
       
         = 
        
       
         1 
        
       
      
        i\%k=1 
       
      
    i%k=1,那么说明是新一段的第一个数,可以从之前的任意一种颜色转移,但是为了避免复杂度退化成 
     
      
       
        
        
          n 
         
        
          3 
         
        
       
      
        n^3 
       
      
    n3,加个小优化,维护一下和就行。
signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	
	int T;
	cin >> T;
	while(T--) {
		int n, k; cin >> n >> k;
		vector<int> c(n + 1);
		for(int i = 1; i <= n; i++) cin >> c[i];
		vector<vector<mint>> dp(n + 1, vector<mint> (n + 1, 0));
		vector<vector<bool>> ok(n + 1, vector<bool> (n + 1));
		dp[0][0] = 1;
		ok[0][0] = true;
		vector<bool> can(n + 1);
		can[0] = true;
		vector<mint> sum(n + 1);
		sum[0] = 1;
		for(int i = 1; i <= n; i++) {
			for(int j = i; j >= 1; j--) {
				if(j % k == 1) {
					if(can[j / k]) {
						ok[j][c[i]] = true;
						dp[j][c[i]] += sum[j / k];
					}
				} else {
					if(ok[j - 1][c[i]]) {
						dp[j][c[i]] += dp[j - 1][c[i]];
						ok[j][c[i]] = true;
					}
				}
				if(j % k == 0 && ok[j][c[i]]) {
					can[j / k] = true;
					sum[j / k] += dp[j - 1][c[i]];
				}
			}
		}
		for(int i = n / k; i >= 0; i--) {
			if(can[i]) {
				cout << sum[i] << '\n';
				break;
			}
		}
	}
 
    return 0;
}










