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;
}