1 条题解
-
0
注意到数据范围达到了 ,且内存较小,启发我们使用 做法。
考虑两个罪犯走的路径是什么样的,罪犯抢劫的房子是他走过的房子的子序列,两个罪犯的终点相同,起点颜色相同。
思考罪犯开始时所在房子颜色相同这个怎么维护,我们记 表示与第 个房子颜色相同的下标最大值。
从贪心的角度考虑,只要保证一段路径两边有一对颜色相同的点对,这条路径存在起点,所以我们可以对 取前缀 。
依旧从贪心的角度考虑,显然有左边的罪犯的第一个抢劫房子的下标越大越好,右边反之。
接着考虑怎么维护子序列中第一个下标最值,不妨借鉴 LIS 的处理方法,因为每个罪犯对每种颜色的房子分别最多抢劫一次,可以直接把 视为 再用 LIS 处理。我们记 表示终点是 号房子时第 对应的 的下标最值,不难想到转移如下。
当 时,有 。
当 时,有 。
因为 只与 有关,可以滚动。
因为有两段路径,我们用 记录以 为左边罪犯以 号房子为终点的子序列中第一个下标最值。
第二段倒着处理就行。
代码:
#include<bits/stdc++.h> using namespace std; int read(){ int x=0,f=1; char c=getchar(); while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {x=(x*10)+c-'0';c=getchar();} return x*f; }//快读 const int N=1e6+5; int n,k,m,l,x[N],y[N],c[N],lt[N],pr[N],d[N]; vector<int> ans; signed main(){ n=read(),k=read(); for(int i=1;i<=n;i++) {c[i]=read();lt[c[i]]=i;} for(int i=1;i<=n;i++) pr[i]=max(pr[i-1],lt[c[i]]); m=read(),l=read(); for(int i=1;i<=m;i++) x[i]=read(),y[x[i]]=i;//y[]用来存x[i]对应的下标 for(int i=1;i<=n;i++) lt[i]=0; for(int i=1;i<=n;i++) { if(c[i]==x[1]) lt[1]=i; else lt[y[c[i]]]=lt[y[c[i]]-1]; if(lt[m]) d[i]=lt[m];//统计 }//第一段 memset(x,0,sizeof(x));memset(y,0,sizeof(y)); for(int i=1;i<=l;i++) x[i]=read(),y[x[i]]=i; for(int i=1;i<=n;i++) lt[i]=0;//初始化 for(int i=n;i>=1;i--) {//倒着 if(c[i]==x[1]) lt[1]=i; else lt[y[c[i]]]=lt[y[c[i]]-1]; if(lt[l]&&d[i]&&c[i]==x[l]) { int z=d[i]-1,p=lt[l]; if(pr[z]>p) ans.push_back(i);//统计 } }//第二段 printf("%d\n",(int)ans.size()); sort(ans.begin(),ans.end()); for(int x:ans) printf("%d ",x); return 0; }
- 1
信息
- ID
- 5493
- 时间
- 1000ms
- 内存
- 164MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者