LOJ.2585.[APIO2018]新家(二分 線段樹 堆)


LOJ
洛谷
UOJ
BZOJ

四OJ Rank1 hhhha

表示這個b我能裝一年→_→

首先考慮離線,將詢問按時間排序。對於每個在\([l,r]\)出現的顏色,拆成在\(l\)加入和\(r+1\)刪除兩個操作,也按時間排序。

對於詢問\((x,t)\),就是求\(t\)時刻,離\(x\)最遠的顏色到\(x\)的距離,也就是從\(x\)出發往左右至少要走多遠才能經過所有顏色。
考慮二分答案。那么就成了,求所有顏色是否都在\([x-mid,x+mid]\)中出現過。

對於這種是否出現過/只計算一次的問題,通常是對每種顏色計算從左到右第一個出現的顏色。
對每個位置\(i\)\(pre_i\),表示\(col_i\)上次出現的位置。那么\(i\)\(col_i\)顏色中,該區間第一個出現的當且僅當\(pre_i<l\)
所以我們對區間求\(pre_i<l\)的位置個數就是答案了。但這好像要樹套樹。。於是復雜度就成了\(O(n\log^3n)\)。。

顯然有點想偏。再看我們要求的問題:區間中是否出現過所有顏色。我們不需要求有多少種顏色出現了,只要能找到一種不在區間中出現過的顏色就可以了。
如果一種顏色不在\([l,r]\)中出現過,那么它的\(pre_i<l\)\(i>r\)。也就是說我們求\([r+1,n]\)中是否存在\(pre_i<l\)就可以了,即求\(pre_i\)的最小值。
每種顏色的\(pre_i\)可以開\(k\)\(set\)維護。
因為同一個位置可以有多種顏色,每個位置的\(pre_i\)會有很多且可能相同。所以對於每個位置還要用一個\(multiset\)或堆來維護\(\min\{pre_i\}\)並支持刪除。

這樣就OK啦,復雜度\(O(n\log^2n)\)

再考慮一下二分能否直接在線段樹上二分。實際上是可以的。
orz kcz
二分一個\(mid\),如果\(Ans\geq mid\),則\((x-mid,x+mid)\)中不含所有顏色,即\([x+mid,n]\)中最小的前驅\(mn\)滿足\(mn\leq x-mid\)
我們實際是要求一個最大的\(i\),使得\([i,n]\)中最小的前驅\(mn\),仍滿足\(mn+i\leq 2x\)\(i\)越大則\(mn\)越大,越容易不滿足條件)。此時答案就是\(\min\{i-x,\ x-\min\{pre_i\}\}\)(一個是右端點一個是左端點)。
怎么在線段樹上求最大的\(i\)呢。
先判一下無解情況。
假設現在是在線段樹的\([l,r]\)區間:
\(x\)落在\([mid+1,r]\)區間,則\(i\)也一定落在\([mid+1,r]\)區間。
\(x\)落在\([l,mid]\)區間,則要判斷一下\(i\)能否落在\([mid+1,r]\)區間。因為\(i\)越大\(mn\)越大,所以只需要判下\(i=mid+1\)時是否可行就行了。

這樣就一個\(\log\)啦。

注意求的\(\min\)\([i,n]\)的,如果遞歸到\([l,mid]\)要與右區間取\(\min\)
另外線段樹上的節點以及\(mn\)是離散化后的值域,比較的時候用\(ref[mid]\)(實際值)與\(x\)比較。

把一個Delete寫成Insert
還有st[col]寫成st[p]
別的就和我四個小時前寫的差不多了?==
我這調的四個小時究竟在干什么==

//BZOJ:55576kb	5916ms	LOJ:35911ms	69728K Luogu:5049ms	88.89MB
#include <set>
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define INF 1000000000
typedef long long LL;
const int N=3e5+7;

int n,ref[N];
std::multiset<int> st[N];
char IN[MAXIN],*SS=IN,*TT=IN,OUT[3000000],*O=OUT;
struct Node
{
	int x,type,t;
	bool operator <(const Node &x)const
	{
		return t<x.t;
	}
}A[N<<1];
struct Quries
{
	int x,t,id;
	bool operator <(const Quries &x)const
	{
		return t<x.t;
	}
}q[N];
struct Heap
{
	std::priority_queue<int,std::vector<int>,std::greater<int> > a,b;
//	inline int Top() {return a.top();}
	inline void Insert(int x) {a.push(x);}
	inline void Delete(int x)
	{
		if(a.top()!=x) b.push(x);
		else
		{
			a.pop();
			while(!b.empty()&&a.top()==b.top()) a.pop(),b.pop();
		}
	}
}hp[N];
struct Segment_Tree
{
	#define ls rt<<1
	#define rs rt<<1|1
	#define lson l,m,ls
	#define rson m+1,r,rs
	#define S N<<2
	int mn[S];
	#undef S
	#define Update(rt) mn[rt]=std::min(mn[ls],mn[rs])
	void Init(const int n)
	{
		for(int i=n<<2; i; --i) mn[i]=n;
	}
//	void Build(int l,int r,int rt)
//	{
//		if(l==r) {mn[rt]=hp[l].a.top(); return;}
//		int m=l+r>>1; Build(lson), Build(rson), Update(rt);
//	}
	void Modify(int l,int r,int rt,int p)
	{
		if(l==r) {mn[rt]=hp[l].a.top(); return;}
		int m=l+r>>1;
		p<=m?Modify(lson,p):Modify(rson,p);
		Update(rt);
	}
//	int Query(int l,int r,int rt,int x,int mnv)
//	{
//		if(l==r) return std::min(ref[l]-x,x-std::min(mnv,ref[mn[rt]]));
//		int m=l+r>>1;
//		if(x>ref[m] || ref[m]+1+std::min(mnv,ref[mn[rs]])<=x<<1) return Query(rson,x,mnv);
//		return Query(lson,x,std::min(mnv,ref[mn[rs]]));
//	}
	int Query(int x)
	{
		int l=1,r=n,rt=1,mnv=INF;
		while(l!=r)
		{
			int m=l+r>>1;
			if(x>ref[m]||ref[m]+1+std::min(mnv,ref[mn[rs]])<=x<<1) l=m+1, rt=rs;
			else mnv=std::min(mnv,ref[mn[rs]]), r=m, rt=ls;
		}
		return std::min(ref[l]-x,x-std::min(mnv,ref[mn[rt]]));
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
void print(int x)
{
	static char obuf[13];
	if(x<0) x=-x, *O++='-';
	if(x)
	{
		int t=0; while(x) obuf[++t]=x%10+48, x/=10;
		while(t) *O++=obuf[t--];
	}
	else *O++='0';
}
//void print(int x)
//{
//	if(x<0) x=-x, *O++='-';
//	if(x>9) print(x/10);
//	*O++ = x%10+48;
//}
int Discrete(int n)
{
	static std::pair<int,int*> tmp[N<<1];
	for(int i=1; i<=n; ++i) tmp[i]=std::make_pair(A[i].x,&A[i].x);
	std::sort(tmp+1,tmp+1+n);
	int cnt=1; ref[*tmp[1].second=1]=tmp[1].first;
	for(int i=2; i<=n; ++i)
		ref[*tmp[i].second=tmp[i].first==tmp[i-1].first?cnt:++cnt]=tmp[i].first;
	return cnt;
}
void Solve(int p,int col,int &tot)
{
	static int tm[N];
	if(col>0)
	{
		tot+=!tm[col]++;
		std::multiset<int>::iterator it=st[col].lower_bound(p);//話說寫set類型的迭代器竟然也對。。
		int nxt=*it, pre=it==st[col].begin()?0:*--it;
		hp[p].Insert(pre), T.Modify(1,n,1,p);
		hp[nxt].Delete(pre), hp[nxt].Insert(p), T.Modify(1,n,1,nxt);//nxt最大也就是n,不會越界 
		st[col].insert(p);
	}
	else
	{
		col=-col, tot-=!--tm[col];
		std::multiset<int>::iterator it=st[col].find(p);
		int pre=it==st[col].begin()?0:(--it,*it++);
		hp[p].Delete(pre), T.Modify(1,n,1,p);
		st[col].erase(it++);
		hp[*it].Delete(p), hp[*it].Insert(pre), T.Modify(1,n,1,*it);
	}
}

int main()
{
	static int Ans[N];

	int n=read(),K=read(),Q=read(),cnt=0;
	for(int i=1,x,type; i<=n; ++i)
		x=read(), type=read(), A[++cnt]=(Node){x,type,read()}, A[++cnt]=(Node){x,-type,read()+1};
	std::sort(A+1,A+1+cnt);
	n=Discrete(cnt), ref[0]=-INF, ref[++n]=INF, ::n=n;

	for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
	std::sort(q+1,q+1+Q);

	for(int i=1; i<=n; ++i) hp[i].Insert(n);
	for(int i=1; i<=K; ++i) hp[n].Insert(0), st[i].insert(n);
//	T.Build(1,n,1); A[cnt+1].t=INF;
	T.Init(n), T.Modify(1,n,1,n), A[cnt+1].t=INF;
	for(int i=1,now=1,tot=0; i<=Q; ++i)
	{
		while(A[now].t<=q[i].t) Solve(A[now].x,A[now].type,tot), ++now;
		Ans[q[i].id]=tot==K?T.Query(q[i].x):-1;
	}
	for(int i=1; i<=Q; ++i) print(Ans[i]), *O++='\n';//printf("%d\n",Ans[i]);
	fwrite(OUT,1,O-OUT,stdout);

	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM