Criminals
POI2014
题意
1.有一个颜色序列
2.有两个人会分别从左往右和从右往左走,并在途中任意取走几个格子里面的颜色,直到两个人相遇。
3.已知两个人所取走的颜色序列,并且保证这两 个颜色序列的最后一个元素都是同一种颜色(代表两个人相遇的点)。
4.两个人出发点的颜色都是相同的,并且他们不会取出发点的颜色.
5.不存在任意一个颜色在这两个序列中出现两次(但可以分别在两个序列中出现一次)。
6.问可能的相遇点有多少个,是那些点。
解
1.枚举每种颜色的点对
(颜色相同情况下找相离最远的,即左边起第一次出现的位置,与右边起第一次出现的位置)
2.需要找到他们最近在哪里可以满足各自的颜色序列。
①预处理每个颜色的后继(对于1来说),
②预处理每个颜色的前驱(对于2来说)
③预处理每个点向后(前)最早碰到序列(取色序列)中第一个元素的位置
用并查集查找,这样同一段序列只会被扫描一次
(找到当前颜色上,第一个颜色序列的位置,并查集查找的颜色序列末尾)
具体代码
#include<bits/stdc++.h>
using namespace std;
const int M=1000005;
int n,K,A[M];
int B[M],C[M],L1,L2;
int nxt[M],pre[M],pr[M],nx[M],pos[M],last[M];
int mark[M],stk[M],top;
int get_nxt(int x) {
top=0;
while(nxt[x]!=x){
stk[++top]=x;
x=nxt[x];
}
while(top)nxt[stk[top--]]=x;
return x;
}
int get_pre(int x) {
top=0;
while(pre[x]!=x){
stk[++top]=x;
x=pre[x];
}
while(top)pre[stk[top--]]=x;
return x;
}
int main() {
scanf("%d %d",&n,&K);
for(int i=1; i<=n; i++) {
scanf("%d",&A[i]);
}
scanf("%d %d",&L1,&L2);
for(int i=1; i<=K; i++)C[i]=pos[i]=0;
for(int i=1; i<=L1; i++) {
scanf("%d",&B[i]);
C[B[i]]=i;
}
for(int i=n; i>=1; i--) {
nx[i]=pos[B[1]];
pos[A[i]]=i;
if(!C[A[i]])continue;
int p=C[A[i]];
if(p==L1)nxt[i]=i;
else nxt[i]=pos[B[p+1]];
}
for(int i=1; i<=K; i++)C[i]=pos[i]=0;
for(int i=1; i<=L2; i++) {
scanf("%d",&B[i]);
C[B[i]]=i;
}
for(int i=1; i<=n; i++) {
pr[i]=pos[B[1]];
pos[A[i]]=i;
if(!C[A[i]])continue;
int p=C[A[i]];
if(p==L2)pre[i]=i;
else pre[i]=pos[B[p+1]];
}
for(int i=1; i<=K; i++)pos[i]=last[i]=0;
for(int i=1; i<=n; i++) {
if(!pos[A[i]])pos[A[i]]=i;
}
for(int i=n; i>=1; i--) {
if(!last[A[i]])last[A[i]]=i;
}
for(int i=1; i<=K; i++) {
int L=pos[i],R=last[i];
if(!L||!R||L>R)continue;
L=get_nxt(nx[L]);
R=get_pre(pr[R]);
if(!L||!R||L>R)continue;
mark[L]++,mark[R+1]--;
}
int cnt=0,res=0;
for(int i=1; i<=n; i++) {
res+=mark[i];
if(res&&A[i]==B[L2])cnt++;
}
printf("%d\n",cnt);
res=0;
for(int i=1; i<=n; i++) {
res+=mark[i];
if(res&&A[i]==B[L2])printf("%d ",i);
}
return 0;
}