[核桃] - P10770 反转子区间

[核桃] - P10770 反转子区间

大意

一个长度为 \(n\) 的整数序列 \(a\)。操作规则:你可以选择若干个互不相交的区间,对每个选中的区间,把里面的数字顺序颠倒,所有翻转同时进行,得到新序列 \(b\)

还原点:如果某个位置 \(i\) 在翻转后 \(b[i]\) 等于原来的 \(a[i]\),这个位置就是还原点。

任务:给定 \(n, k\) 和序列 \(a\),问有多少种选择不相交区间的方法,使得最终恰好有 \(k\) 个还原点,只要选择的区间集合不同,就算不同方案(不管翻转后序列是否一样)

思路

对于翻转后找还原点的这个问题,我们观察数据范围,发现 \(n \le 500\),那么我们可以大胆猜是 \(O(n^3)\) 的时间复杂度,因为看着很像区间 dp,因为我们要找不同的区间去选择,我们可以定义这样的状态: \(dp_{ij}\) 表示长度为 \(i\) 的区间,恰好有 \(j\) 个还原点,那么我们在转移时应该怎么考虑呢?

先初始化,使 \(dp[0][0] = 1\),然后我们肯定要枚举在第 \(i\) 的地方,还原点为 \(j, j\in [0, i]\) 的所有转移,而我们对 \(i\) 这个地方分类讨论,对于这个题目,我们要求的是所有区间都不相交,那么第 \(i\) 个点就只有一下三种情况:

  1. \(i\) 个点不被翻转

  2. \(i\) 个点单独自己翻转

  3. \(i\) 个点被前面的区间所包含

于是我们针对这三种情况进行分类讨论,我们发现在 \(j > 0\) 是才有翻转的情况,如果是第 \(1\)\(2\) 种情况的话,当前的 \(i\) 都是一个还原点,所以要从 \(i - 1,j - 1\) 去转移,因为区间的选择不同是不同的方案,那么:

情况 \(1,2\)\(dp[i][j] \to dp[i][j] + dp[i - 1][j - 1] * 2\)

然后对于第三种情况,我们显然是不知道第 \(i\) 个点是被包含到哪个区间内的,所以我们直接枚举区间的起点 \(l\) 即可,那么就是 \([l, i]\) 这个区间,对于这个区间我们需要知道这个区间在反转后有几个还原点,我们直接预处理出来即可,预处理 \(c[i][j]\) 表示在 \([i,j]\) 这个区间内有几个还原点,然后就可以直接转移了,但是要注意,如果当 \([l, i]\) 这个区间内的还原点数量超过了 \(j\),就无法转移。

情况 \(3\)\(dp[i][j] \to dp[i][j] + dp[l - 1][j - c[l][i]]\)

最后输出的答案即为 \(dp[n][k]\)

代码

#include<iostream>
using namespace std;

#define ll long long
const int MAXN = 505;
const int MOD = 1e9 + 7;

int a[MAXN], n, k;
int c[MAXN][MAXN];
int dp[MAXN][MAXN];

int main(){
	freopen("reverse.in", "r", stdin);
	freopen("reverse.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	cin >> n >> k;
	
	for(int i = 1;i <= n;i ++) {
		cin >> a[i];
	}
	
	for(int i = 1;i <= n;i ++){
		for(int j = i;j <= n;j ++){
			int len = j - i + 1;
			if(len % 2 == 1){
				c[i][j] ++;
			}
			for(int p = 1;p <= len / 2;p ++){
				int x = a[i + p - 1], y = a[j - p + 1];
				if(x == y){
					c[i][j] += 2;
				}
			}
//			cout << i << ' ' << j << ' ' << c[i][j] << '\n';
		}
	}
	dp[0][0] = 1;
	for(int i = 1;i <= n;i ++){
		for(int j = 0;j <= i;j ++){
			if(j > 0) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] * 2) % MOD;
			for(int l = 1;l <= i - 1;l ++){
//				cout << j - cnt[l][i] << '\n';
				if(j - c[l][i] < 0) continue;
				dp[i][j] = (dp[i][j] + dp[l - 1][j - c[l][i]]) % MOD;
			}
		}
	}
	cout << dp[n][k] % MOD << '\n';
	return 0;
}
posted @ 2025-12-16 22:21  To_Carpe_Diem  阅读(8)  评论(0)    收藏  举报