ZOJ3967 : Card Game

比赛的时候因为卡内存,在抠内存的时候改错了,导致赛内没有AC,赛后发现数组开的很小都可以AC。

 

分析题意我们发现,这题需要求出所有存在的直线形成的上凸壳,那么查询$[L,R]$时在凸壳上二分导数,找到最大值即可。

因为有删除操作,故离线求出每条直线存在的时间区间,在时间线段树上打标记,那么这样会转化成$O(n\log n)$次插入。

那么现在需要维护一个数据结构,支持插入直线,询问单点最值,这显然可以使用李超线段树。

沿着时间线段树进行dfs,用一个栈按时间记录所有修改,那么可以很方便地实现李超线段树的还原。

时间复杂度$O(n\log^2n)$。

 

#include<cstdio>
#include<algorithm>
#include<stack>
#include<map>
using namespace std;
typedef pair<int,int>P;
typedef long long ll;
const int N=100010,M=1500000,E=200010;
const int eps=5;
const int inf=1000000000;
const ll offset=2000000000LL;
const ll infll=1LL<<62;
int Case,n,m,i,op,x,y;
int st[N],en[N],cur;
int cq,que[N][2],g[N],nxt[N];
ll ans[N];
int G[131111],V[M],NXT[M],ED;
map<P,stack<int> >idx;

int root,tot,l[E],r[E],val[E];

int pool[E][2],cpool;

struct LINE{
	int k,b;
}a[N];

inline ll cal(int x,int p){return 1LL*a[x].k*p+a[x].b;}

inline void addedge(int x,int y){
	nxt[y]=g[x];g[x]=y;
}
void build(int x,int a,int b){
	G[x]=0;
	if(a==b)return;
	int mid=(a+b)>>1;
	build(x<<1,a,mid);
	build(x<<1|1,mid+1,b);
}
void tag(int x,int a,int b,int c,int d,int p){
	if(c<=a&&b<=d){
		V[++ED]=p;
		NXT[ED]=G[x];
		G[x]=ED;
		return;
	}
	int mid=(a+b)>>1;
	if(c<=mid)tag(x<<1,a,mid,c,d,p);
	if(d>mid)tag(x<<1|1,mid+1,b,c,d,p);
}
void ins(int&x,int a,int b,int p,int f){
	if(!val[x]){
		x=++tot;
		l[x]=r[x]=0;
		pool[++cpool][0]=x;
		pool[cpool][1]=-f;
		val[x]=p;
		return;
	}
	if(cal(p,a)<cal(val[x],a)&&cal(p,b)<cal(val[x],b)){
		pool[++cpool][0]=x;
		pool[cpool][1]=val[x];
		val[x]=p;
		return;
	}
	if(cal(p,a)>=cal(val[x],a)&&cal(p,b)>=cal(val[x],b)){
		return;
	}
	if(a==b)return;
	ll mid=((offset+a+b)>>1)-inf;
	ins(l[x],a,mid,p,x);
	ins(r[x],mid+1,b,p,x);
}
inline void umin(ll&a,ll b){if(a>b)a=b;}
inline void umax(ll&a,ll b){if(a<b)a=b;}
ll TMP;
void ask(int x,int a,int b,int c){
	if(!val[x])return;
	umin(TMP,cal(val[x],c));
	if(a==b)return;
	ll mid=((offset+a+b)>>1)-inf;
	if(c<=mid)ask(l[x],a,mid,c);else ask(r[x],mid+1,b,c);
}
inline void retrace(int pos){
	while(cpool>pos){
		int x=pool[cpool][0],y=pool[cpool--][1];
		val[x]=y;
		if(y<=0){
		  tot--;
		  if(y<0){
		    if(l[-y]==x)l[-y]=0;
		    if(r[-y]==x)r[-y]=0;
		  }
		}
	}
}
inline ll query(int L,int R){
	//find max t that f(t)>f(t-1)
	int o=L++;
	while(L<=R){
		ll mid=((offset+L+R)>>1)-inf;
		TMP=infll;
		ask(root,-inf,inf,mid);
		ll u=TMP;
		TMP=infll;
		ask(root,-inf,inf,mid-1);
		if(u>TMP)L=(o=mid)+1;else R=mid-1;
	}
	TMP=infll;
	ask(root,-inf,inf,o);
	return TMP;
}
void dfs(int x,int a,int b){
	int pos=cpool;
	for(int i=G[x];i;i=NXT[i]){
		ins(root,-inf,inf,V[i],0);
	}
	if(a==b){
		for(int i=g[a];i;i=nxt[i]){
			ans[i]=query(que[i][0],que[i][1]);
		}
		retrace(pos);
		return;
	}
	int mid=(a+b)>>1;
	dfs(x<<1,a,mid);
	dfs(x<<1|1,mid+1,b);
	retrace(pos);
}
int main(){
	scanf("%d",&Case);
	while(Case--){
		scanf("%d%d",&n,&m);
		cur=1;
		for(i=1;i<=n;i++){
			scanf("%d%d",&a[i].k,&a[i].b);
			idx[P(a[i].k,a[i].b)].push(i);
			st[i]=1;
		}
		for(i=1;i<=m;i++){
			scanf("%d%d%d",&op,&x,&y);
			if(op==0){
				cq++;
				que[cq][0]=x;
				que[cq][1]=y;
				addedge(cur,cq);
			}
			if(op==1){
				cur++;
				a[++n].k=x;
				a[n].b=y;
				st[n]=cur;
				idx[P(x,y)].push(n);
			}
			if(op==2){
				int z=idx[P(x,y)].top();
				idx[P(x,y)].pop();
				en[z]=cur++;
			}
		}
		for(i=1;i<=n;i++)if(!en[i])en[i]=cur;
		build(1,1,cur);
		for(i=1;i<=n;i++)tag(1,1,cur,st[i],en[i],i);
		dfs(1,1,cur);
		for(i=1;i<=cq;i++)printf("%lld\n",ans[i]);
		
		cq=0;
		for(i=1;i<=n;i++)en[i]=0;
		for(i=1;i<=cur;i++)g[i]=0;
		ED=0;
		idx.clear();
	}
}

  

posted @ 2017-04-23 00:42  Claris  阅读(351)  评论(0编辑  收藏  举报