题意概括:
本题题意是求给定的数组中范围内都认识(满足条件)的子序列的数量。
思路:
我们可以先分析如果用dp来解,那么我们怎么表示状态,通过题目分析我们可以用 f[i] 来进行状态表示,其中,f[ i ] 指的是以 i 结束的满足条件的子序列的个数。而对于状态转移而言,我们对于每一个 i ,只有两种处理,一种是它没有不认识的,另一种是它有认识的,因为 i 本身就是一个符合条件的答案,所以我们对于每一个 i 都要先取为1,然后如果 i 没有认识的,那么就这样更新:f[i] = f[i - 1] + 1, 反之,就用if(i >= w[i])f[i] = i - w[i] 更新,其中,我们会先将有不认识的 i 进行预处理,使它对应的是左边最大的不认识的, 然后就可以使用如上的状态转移方程,最后将从1 到 n 的所有个数加起来输出即可。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int f[N], w[N];
int main()
{
int t;
cin >> t;
while (t--)
{
int n, m;
scanf("%d%d", &n, &m);
memset(w, 0, sizeof w);
for (int i = 0; i < m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
if(x > y) {int k= x; x = y, y = k;} //记录所有的不认识情况
w[y] = w[y] > x ? w[y] : x;
}
int ma = 0;
for(int i = 1; i <= n; ++ i){
ma = ma < w[i] ? w[i] : ma;
w[i] = w[i] > ma ? w[i] : ma; //预处理使得i对应左边最大不符合的数
}
for (int i = 1; i <= n; ++i){
f[i] = 1;
if (w[i] == 0) f[i] = f[i - 1] + 1; //dp状态转移过程
else if(i >= w[i])f[i] = i - w[i];
}
LL ans = 0;
for (int i = 1; i <= n; ++i) ans += f[i]; //将结果累加输出
cout << ans << endl;
}
return 0;
}