T1:
n个数,m个询问,强制在线,每次给出l,r,求区间mex值
a[i]<=1e9,n,m<=200000
题解:
我为什么这么ZZ
这样的思路以前应该见过啊。
离线的做法?
首先处理出1~i的mex值用线段树维护,同时处理出nxt[i]表示这个数字的下一个位置
当我们把左节点右移的时候,会对i~nxt[i]这个区间造成的影响是:mex比这个数字大的全都要变成这个数字,也就是区间取min值
强制在线的做法?
我们换个思路,建立一棵主席树,每一棵线段树的节点i表示数字i出现的最右端
那么我们对于一个询问l,r,1~r区间内如果有一个前缀,他们所有的节点维护的最右端都>=l,那么说明他们都出现过了,这样的话,如果左区间有一个 < l的位置,那么答案肯定在左区间
这样我们对于这棵主席树维护一个min值,如果min < l说明答案在左边了
还有一个问题就是可以发现只有n个数字,那么mex肯定是0~n之间的一个数字
复杂度
O(nlogn)
O
(
n
l
o
g
n
)
代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int N=200005;
struct hh{int l,r,minn;}t[N*20];
int sz,root[N];
void insert(int &now,int l,int r,int v,int wz)
{
t[++sz]=t[now]; now=sz;
if (l==r)
{
t[now].minn=wz;
return;
}
int mid=(l+r)>>1;
if (v<=mid) insert(t[now].l,l,mid,v,wz);
else insert(t[now].r,mid+1,r,v,wz);
t[now].minn=min(t[t[now].l].minn,t[t[now].r].minn);
}
int qurry(int now,int l,int r,int a)
{
if (l==r) return l;
int mid=(l+r)>>1;
if (t[t[now].l].minn<a) return qurry(t[now].l,l,mid,a);
else return qurry(t[now].r,mid+1,r,a);
}
int main()
{
freopen("mex.in","r",stdin);
freopen("mex.out","w",stdout);
int n,m,t,x;scanf("%d%d%d",&n,&m,&t);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
root[i]=root[i-1];
if (x<n) insert(root[i],0,n,x,i);
}
int ans=0;
for (int i=1;i<=m;i++)
{
int l,r;scanf("%d%d",&l,&r);
if (t==1) l^=ans,r^=ans;
ans=qurry(root[r],0,n,l);
printf("%d\n",ans);
}
}
T2
题解:
首先我们考虑从一个点左右看过去能看到最高的顶点分别是哪个
我们单独考虑左侧,右侧同理,考虑前n个点的上凸壳。第n个点往左看去的最高点实际上就是在上凸壳上的某一个点
所以我们左右分别求一次上凸壳O(n),注意这个点本身就比左边高的情况
然后我们怎么求从每一个点出发需要经过的段数
假设从A号点出发,走到B点时,看到了比A更高的顶点,此时相当于从B重新出发
那么我们让A->B,可以发现这东西是一棵树,这样遍历可以得到答案
现在的问题是对于每一个A,如何快速找到B
定义F[A]表示A点能看到最高的高度,如果A往左走,我们要找到A左侧第一个B满足F[B]>=F[A],右边一样
那么我们按照每个点的F[A]从小到大排序,在原来点的顺序下构建双向链表,每次以F[A]的顺序访问一个点,此时双向链表中的元素的F值均>=F[A],所以我们只需要查询他的左侧点/右侧点就ok,然后删除他
复杂度
O(nlogn)
O
(
n
l
o
g
n
)
记得叉积要开longlong啊
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int N=1000005;
struct po
{
int x,y;
po(int X=0,int Y=0){x=X,y=Y;}
}a[N];
struct hh{int v,t;}p[N];
int stack[N],top,fa[N],f[N],l[N],r[N];
po operator -(po a,po b){return po(a.x-b.x,a.y-b.y);}
bool operator <(hh a,hh b){return a.v<b.v||(a.v==b.v&&(a.t&1)<(b.t&1))||(a.v==b.v&&(a.t&1)==(b.t&1)&&a.t<b.t);}
LL cj(po a,po b){return (LL)a.x*b.y-(LL)b.x*a.y;}
int find(int x)
{
top=0;
while (x) stack[++top]=x,x=fa[x];
for (int i=top-1;i;i--)
{
int now=stack[i];
f[now]=f[fa[now]]+abs(fa[now]-now);
fa[now]=0;
}
}
int main()
{
int n;scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
top=0;
for (int i=1;i<=n;i++)
{
while (top>1 && cj(a[i]-a[stack[top-1]],a[stack[top]]-a[stack[top-1]])<=0) top--;
if (i==1 || a[i].y>=a[stack[top]].y) l[i]=i;else l[i]=stack[top];
stack[++top]=i;
}
top=0;
for (int i=n;i>=1;i--)
{
while (top>1 && cj(a[i]-a[stack[top-1]],a[stack[top]]-a[stack[top-1]])>=0) top--;
if (i==n || a[i].y>a[stack[top]].y) r[i]=i;else r[i]=stack[top];
stack[++top]=i;
}
for (int i=1;i<=n;i++)
if (a[r[i]].y<a[l[i]].y)
{
p[i].v=a[l[i]].y;
p[i].t=i<<1;
}
else
{
p[i].v=a[r[i]].y;
p[i].t=i<<1|1;
}
sort(p+1,p+n+1);
for (int i=1;i<=n;i++)
l[i]=i-1,r[i]=i+1;
for (int i=1;i<=n;i++)
{
int now=p[i].t>>1;
if (p[i].t&1)//往右走
fa[now]=r[now];
else fa[now]=l[now];
l[r[now]]=l[now];
r[l[now]]=r[now];
}
fa[p[n].t>>1]=0;
for (int i=1;i<=n;i++)
if (fa[i]) find(i);
for (int i=1;i<=n;i++) printf("%d\n",f[i]);
}