P4303 [AHOI2006]基因匹配

题意


给定一个两个长度为5n的序列, 保证每个序列1-n各出现5次, 求最长公共子序列
(n leq 20000)

题解


首先, 突破点肯定是每个数只出现5次。
然后我们开始思考这和普通lcs的区别在于: 能和每个数匹配的至多只有5个数
我们再看看朴素的lcs, 朴素的lcs设 (f_{i, j}) 表示到s到i, t到j的最长长度, 假如(S_i == T_j) 那么可以他们匹配。
但事实上: 我们可以假装没学过lcs, 那按照我一贯的dp手法, 一定会分阶段dp;
我们这样理解,fi,j表示s匹配到i, t匹配到j的最大长度,并且i, j一定要匹配起来的最大值
fi就表示我们现在要匹配si,那么我们可以用一个线段树来维护状态,
假如我们完全不匹配, 那么每一个状态就和fi-1,j相等,
这时候我们开始匹配, 对于5各点中的每一个点x, 我们让

[f_{i,x} = max(f_{i-1, x}, max_{j}^{i-1}(f_{i-1, j}) + 1) ]

瞎乱线段树维护一下

#include <iostream>
#include <cstdio>
#define ll long long
#define l(x) ((x)<<1)
#define r(x) (l(x)+1) 
using namespace std;

const int N = 100005;s
int n, m, a[N];
ll tree[N<<2], tag[N<<2];

void push_up(int o){
	tree[o] = tree[l(o)] + tree[r(o)];
}

void f(int o, int l, int r, int v){
	tree[o] = 1ll*(r-l+1)*v;
	tag[o] = v; 
}

void push_down(int o, int l, int r){
	int mid = (l+r)/2;
	f(l(o), l, mid, tag[o]);
	f(r(o), mid+1, r, tag[o]);
	tag[o] = 0;
}

void build(int o, int l, int r){
	if(l==r){
		tree[o] = a[l];
		return ;
	}
	
	int mid = (l+r)/2;
	build(l(o), l, mid);
	build(r(o), mid+1, r);
	push_up(o);
}

ll query(int o, int l, int r ,int ql, int qr){
	if(ql<=l&&r<=qr) return tree[o];
	
	int mid=(l+r)/2;ll res=0;
	push_down(o, l, r);
	if(ql <= mid) res = query(l(o), l, mid, ql, qr);
	if(qr > mid) res = max(res, )query(r(o), mid+1, r, ql, qr));
	return res;
}

void update(int o, int l, int r, int ql, int qr, int v){
	if(ql<=l&&r<=qr){
		f(o, l, r, v);
		return ;
	}
	
	int mid = (l+r)/2;
	push_down(o, l, r);
	if(ql<=mid) update(l(o), l, mid, ql, qr, v);
	if(qr>mid) update(r(o), mid+1, r, ql, qr, v);
	push_up(o);
}

ll query(int l, int r){
	return query(1, 1, n, l, r);
}

ll update(int l, int r, int v){
	update(1, 1, n, l, r, v);
} 

int read(){
	int num=0, flag=1; char c=-1;
	while((c<'0' || c>'9')&&c!='-') c=getchar();
	if(c=='-') flag=-1, c=getchar();
	while(c>='0' && c<='9') num=num*10+c-'0', c=getchar();
	return num*flag; 
} 

int s[N], t[N], p[N][10];

void scan(){
	n=read()*5;
	for(int i=1; i<=n; i++){
		a[i] = 0;
	}
	build(1, 1, n);
	
	for(int i=1; i<=n; i++){
		s[i] = read();
	}
	for(int i=1; i<=n; i++){
		t[i] = read();
		for(int j=1; j<=5; j++){
			if(!p[t[i]][j]){
				p[t[i]][j] = i;
			}
		}
	}
} 

void solve(){
	
} 

int main(){
	scan();
	solve();
	return 0;
} 

什么东西啊, 我自己都看不懂

原文地址:https://www.cnblogs.com/ltdjcoder/p/15229492.html