luogu P4696 [CEOI2011]Matching

luogu P4696 [CEOI2011]Matching

题目大意

讲得比较清楚了吧
我就讲怎么把问题转换吧
p [ i ] p[i] p[i]表示的是排名为 i i i的在第 p [ i ] p[i] p[i]那个位置
然后我们可以令 r k [ i ] rk[i] rk[i]表示第 i i i位的排名位 r k [ i ] rk[i] rk[i],很明显 r k [ p [ i ] ] = i rk[p[i]] = i rk[p[i]]=i
然后就转换为 r k rk rk数组和 a a a进行匹配了
考虑怎么匹配,
p r e [ i ] 表 示       r k [ i ] 在 r k [ 1.... i ] 中 的 排 名 pre[i]表示 \ \ \ \ \ rk[i] 在 rk[1....i]中的排名 pre[i]     rk[i]rk[1....i],然后那个树状数组维护一下就可以把pre搞出来了
然后再用KMP求 n x t nxt nxt
最后把 a a a丢进去匹配就行了,过程和普通的差不多
来看代码ba
code:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define lowbit(x) (x & -x)
#define N 1000005
using namespace std;
int tree[N];
void update(int x, int y){
	for(;x < N; x += lowbit(x)) tree[x] += y;
}
int query(int x){
	int ret = 0;
	for(;x; x -= lowbit(x)) ret += tree[x];
	return ret;
}
int n, m, a[N], rk[N], nxt[N], pre[N], c[N], b[N], ans[N];
int main(){
	scanf("%d%d", &n ,&m);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), rk[a[i]] = i;//转换为rk
	for(int i = 1; i <= n; i ++)
		pre[i] = query(rk[i]), update(rk[i], 1);//转换为pre
	pre[n + 1] = -1;//方便下面的匹配
	memset(tree, 0, sizeof tree);
	int j = 0;
	for(int i = 1; i < n; i ++){//造nxt
 		while(j && pre[j + 1] != query(rk[i + 1])){
 			for(int k = i - j + 1; k <= i - nxt[j]; k ++)
			 	update(rk[k], -1);
		 		j = nxt[j];
		 }
		 if(pre[j + 1] == query(rk[i + 1])) j ++, update(rk[i + 1], 1);
		 nxt[i + 1] = j;
	}
	for(int i = 1; i <= m; i ++) scanf("%d", &b[i]), c[i] = b[i];
	sort(c + 1, c + 1 + m);
	for(int i = 1; i <= m; i ++) b[i] = lower_bound(c + 1, c + 1 + m, b[i]) - c;//把a离散化
	j = 0;
	memset(tree, 0, sizeof tree);
	for(int i = 0; i < m; i ++){//匹配
		while(j && pre[j + 1] != query(b[i + 1])){//看对应的排名是否相同
 			for(int k = i - j + 1; k <= i - nxt[j]; k ++)//不相同就往前跳,记得把失配那一段的从树状数组中清掉,否则会影响排名
			 	update(b[k], -1);
				j = nxt[j];//往前跳
		 }	
		 if(pre[j + 1] == query(b[i + 1])) j ++, update(b[i + 1], 1);//匹配就匹配
		 if(j == n)	ans[++ ans[0]] = i - n + 2;//匹配到了就记录答案,不用往前跳因为之前已经把pre[n+1]设为-1了,所以下一轮会跳
	}
	printf("%d\n", ans[0]);
	for(int i = 1; i <= ans[0]; i ++) printf("%d ", ans[i]);//输出
	return 0;
}

总结

不得不说这真是道KMP好题,写完这题对KMP的认识又深了一个层次
问题一步一步的转换,然后分析,完全不是死套模板,是道思博锻炼思维的题目。
学习一个东西还是不能死套模板,而是要理解里面的原理,巧妙利用和变化。

posted @ 2019-08-10 20:56  lahlah  阅读(22)  评论(0编辑  收藏  举报