「NOIP2018模拟赛」 T4 - 并查集+区间Dp

题目描述

小 Z 上英语课思考数学问题被英语老师发现啦~

英语老师:「你这么爱胡思乱想我问你一道英语题吧」

小 Z 想跑,但是已经来不及了。

英语老师:「我们定义一个回文串是正方读起来相同的字符串」

小 Z:「这个简单,不就是像 " a b b a " "abba" "abba" " a b a " "aba" "aba"这样的吗」

英语老师:「现在给你一个长度为 n n n 的字符串,要你求出他的最长回文子序列」

小 Z:「子序列是不连续的吧? 好的我知道了」

小 Z 轻松的解决了这个问题,并把他修改了一下交给你。
  
现在一个字符串变成了 m m m 个数字,会魔法的小 Z 可以把一个数字 x x x 根据变换规则变成 y y y,给定所有的变换规则,要你求出这个数字串的最长回文子序列。

输入格式

第一行输入 3 个正整数 n n n k k k m m m k k k 是转换规则的个数。

第二行开始的 k 行,每行两个正整数 x x x y y y,表示数字 x x x 可以变成数字 y y y, 并且数字 y y y 可以变成数字 x x x。注意,如果数字 x x x 可以变成数字 y y y,并且数字 y y y 可以变成数字 z z z,那么数字 x x x 也可以便成数字 z z z x x x 可能等于 y y y,同一对 ( x , y ) (x, y) (x,y)可能重复出现。

最后一行输入 m m m 个正整数,表示题目中所提的数字串,每个数 ≤ n \le n n

输出格式

输出一行一个数表示答案。

数据范围

对于 20% 的数据: n , k , m ≤ 10 n,k,m\le10 n,k,m10

对于 40% 的数据: n , k , m ≤ 200 n,k,m\le200 n,k,m200

对于 100% 的数据: n ≤ 1 0 5 , k ≤ 1 0 6 , m ≤ 1 0 3 , 1 ≤ x , y ≤ n n \le 10^5,k \le 10^6,m \le 10^3,1 \le x,y \le n n105k106m1031xyn

分析

首先可以想到的是,对于可以互相转化的数字,把他们都变成一样的数字会有最优解。于是可以用并查集维护,将可以互相转化的数字合并,最后将每个数变成它的祖先。然后可以用Dp。定义 f [ i ] [ j ] f[i][j] f[i][j]为区间 [ i , j ] [i,j] [i,j]内的最长回文子序列,容易知道 f [ i ] [ i ] = 1 f[i][i]=1 f[i][i]=1。然后就可以知道:
f [ i ] [ j ] = max ⁡ { f [ i + 1 ] [ j ] , f [ i ] [ j − 1 ] , f [ i + 1 ] [ j − 1 ] + 2 ( a [ i ] = = b [ j ] ) } f[i][j]=\max\{f[i+1][j],f[i][j-1],f[i+1][j-1]+2(a[i]==b[j])\} f[i][j]=max{f[i+1][j],f[i][j1],f[i+1][j1]+2(a[i]==b[j])}
然后就可以愉快的 A C AC AC

代码

#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int N=1e5+5;
int f[N];
int d[1005][1005];
int n,k,m;
int a[1005],ans;
int getf(int x) {
	return f[x]==x?x:f[x]=getf(f[x]);
}
int main() {
//	freopen("d.in","r",stdin);
//	freopen("d.out","w",stdout);
	scanf("%d%d%d",&n,&k,&m);
	for (int i=1;i<=n;i++) {
		f[i]=i;
	}
	for (int i=1;i<=k;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		u=getf(u);
		v=getf(v);
		if (u!=v) f[v]=u;
	}
	for (int i=1;i<=m;i++) {
		scanf("%d",&a[i]);
		a[i]=getf(a[i]);
		d[i][i]=1;
	}
	for (int len=2;len<=m;len++) {
		for (int i=1;i+len-1<=m;i++) {
			int j=i+len-1;
			if (a[i]==a[j]) d[i][j]=d[i+1][j-1]+2;
			else d[i][j]=max(d[i][j-1],d[i+1][j]);
		}
	}
	printf("%d",d[1][m]);
	return 0;
}

你可能感兴趣的:(数据结构---并查集,动态规划---区间Dp,模拟赛)