https://www.luogu.org/problemnew/show/P2564
题意:中文题,就不怎么说了。
是这道题的强化版 链接。
做法:仿照上一道题的做法,首先增加右段点,知道出现的物品种类大于总数,然后开始开始检查左端点,是否可以往右移动。
可以移动的条件就是判断,当前点上的物品是否在后面出现过。
虽然思路听起来和上一道题差不多,不难,但不过这道题还是有点烦的,首先你要对点进行离散化,然后进行储存,如用vector会超时,所以这里采用邻接表来储存信息,就不会T了,具体还有什么坑,也只有自己去做了才知道了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int N=1e6+1110; int n,m,a[N],sum[N],pos[N]; int g[N],b[N],gcnt,len,bcnt; struct node { int v,nxt; } ed[N]; int head[N],ecnt; void add(int u,int v) { ed[ecnt].v=v; ed[ecnt].nxt=head[u]; head[u]=ecnt++; } inline int getid(int x) { int t=lower_bound(g,g+len,x)-g+1; return t; } int main() { gcnt=bcnt=0; ecnt=1; scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { int T,x; scanf("%d",&T); b[bcnt++]=T; while(T--) { scanf("%d",&x); b[bcnt++]=x; g[gcnt++]=x; } } sort(g,g+gcnt); len=unique(g,g+gcnt)-g; int mark=1; for(int i=0; i<bcnt;) { int po=b[i]; for(int j=1; j<=po; j++) { int pp=getid(b[i+j]); add(pp,mark); } mark++; i=i+po+1; } int cnt=0,l=1,mx=INT_MAX; ///memset(pos,0,sizeof(pos)); for(int i=1; i<=len; i++) { for(int j=head[i]; j!=0; j=ed[j].nxt) { if(pos[ed[j].v]==0) cnt++; pos[ed[j].v]=i; } while(l<i) { int t=0,ss=0; for(int j=head[l]; j!=0; j=ed[j].nxt) { if(l<pos[ed[j].v]) t++; ss++; } if(t==ss) l++; else break; } if(cnt>=m&&g[i-1]-g[l-1]<mx) { mx=g[i-1]-g[l-1]; } } printf("%d\n",mx); return 0; }