題目:
給n個線段:[ L , R]
給m個線段:[ A , B ] 權值爲C
(1<= n , m <= 200000) (0<=L<=R<=10^9) (0<=A<=B<=10^9) (1<=C<=10^9)
要求:在兩組線段中各選一個,使得兩個線段的相交部分[ X,Y] 滿足 (Y-X)*C 的值最大,求這個最大值以及選擇的是哪一對線段。
一類線段帶權值,一類不帶權值,所以,對於每個帶權值的線段,找出跟它相交長度最大的線段,遍歷就行了。
直接做是O(mn)會超時,所以需要用到掃描線,這個掃描線還是比較複雜的。
首先,線段相交分爲以下四種情況:
將所有線段都加入掃描線,每條線段都出現兩次:一次入邊, 一次出邊。
以下就用L,R代表無權值線段的入邊,出邊。用A,B代表有權值線段的入邊,出邊。
掃描線的過程,就是將所有的A,B,L,R排序,然後從小到大掃描一遍。
並且,當座標一樣時,先進行L,R操作(更新數據結構記錄的信息),再進行A,B操作(A,B的操作其實就是更新最終答案)。
那麼,整個過程實際上,就是在A,B,L,R這四個端點各需要進行什麼操作的問題。
以下,設當前座標爲X,按座標從小到大掃描。L,R,A,B中未列出的操作表示不操作。
情況一: 維護覆蓋了X點的LR線段中,可以達到的最大R值是多少 O(n)L:更新最大的R值
A:若最大R值大於等於B,更新答案。
這種方法實際上,可以計算所有覆蓋了A端點的答案。
情況二: O(n log2(n))
R:將R-L這個值加入L的位置。O(log2(n))
B:在區間[A,B]中查找最大值。O(log2(n))
這樣做可以找出所有AB覆蓋LR的線段,是因爲碰到了R端點,纔會將R-L加入L位置。
所以,碰到B端點時,在區間[A,B]中,搜到的所有的線段,都是L>=A 並且 R<=B的LR線段。
情況三:
可以跟情況一用同一種方法來做,情況四需要反向掃描。
另一種辦法是用平衡樹:O(n log2(n))
L:將本線段R值加入平衡樹
R:將R值從平衡樹中刪除
A:找到平衡樹中的最大值,計算。
在A點的平衡樹的線段LR中,L<=A 並且 R > A
情況四:
L:將L值加入平衡樹
R:將L值從平衡樹中刪除
B:找到平衡樹中最小值,計算。
在B點的平衡樹的線段LR中,L<=B 並且 R > B
在代碼中,將情況一跟三合併成了覆蓋了A端點的答案。
所以代碼中分了三類:
一:AB包含LR的情況 L>=A R<=B -- 用線段樹維護,單次操作O(log2(n)) 總複雜度 O(n log2(n))
二:LR覆蓋了A點的情況 L<=A R>A --單次操作:平衡樹O(log2(n)) 或情況一中描述的方法O(1)
三:LR覆蓋了B點的情況 L<=B R>B --同第二類。
第一類就用線段樹O(n*log2(n))
如果二,三類,使用情況一描述的O(n)方法,第二類需要從左往右掃描,第三類需要從右往左掃描,但是都是O(n)複雜度。
如果二,三類,使用平衡樹,則只需要從左往右掃描一次,但是總複雜度是O(n*log2(n))
實測:平衡樹是982ms 另一種方法是561ms。
平衡樹代碼:(982ms 49300KB)
/*
982 ms 49300 KB
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define maxn 200007
#define LL long long
using namespace std;
//SBT(Size Balanced Tree)
struct Node{
int V,ID;
Node():V(0),ID(0){}
Node(int V,int ID):V(V),ID(ID){}
bool operator <(const Node &B)const{return V < B.V || V==B.V && ID < B.ID;}
bool operator ==(const Node &B)const{return V==B.V&&ID==B.ID;}
}K[maxn<<1];
int L[maxn<<1],R[maxn<<1],S[maxn<<1],IP,TA,TB;
void zig(int &x){int t=R[x];R[x]=L[t];L[t]=x;S[t]=S[x];S[x]=S[L[x]]+S[R[x]]+1;x=t;}
void zag(int &x){int t=L[x];L[x]=R[t];R[t]=x;S[t]=S[x];S[x]=S[L[x]]+S[R[x]]+1;x=t;}
void lev(int &x,bool flag){
if(flag){
if(S[L[L[x]]]>S[R[x]]) zag(x);
else if(S[R[L[x]]]>S[R[x]]) {zig(L[x]);zag(x);}
else return;
}
else{
if(S[R[R[x]]]>S[L[x]]) zig(x);
else if(S[L[R[x]]]>S[L[x]]){zag(R[x]);zig(x);}
else return;
}
lev(L[x],true);lev(R[x],false);
lev(x,true);lev(x,false);
}
void Insert(int &rt,Node X){
if(!rt) {rt=++IP;L[rt]=R[rt]=0;S[rt]=1;K[rt]=X;return;}
X < K[rt]?Insert(L[rt],X):Insert(R[rt],X);
++S[rt];lev(rt,X < K[rt]);
}
Node Delete(int &rt,Node X){
Node Del;--S[rt];
if(X == K[rt] || X < K[rt] && !L[rt] || K[rt] < X && !R[rt]){
Del=K[rt];
if(!L[rt]||!R[rt]) rt=L[rt]+R[rt];
else K[rt]=Delete(L[rt],X);
}
else Del=X < K[rt]?Delete(L[rt],X):Delete(R[rt],X);
lev(rt,K[rt]<X);
return Del;
}
int Min(int &rt){return L[rt]?Min(L[rt]):rt;}
int Max(int &rt){return R[rt]?Max(R[rt]):rt;}
//非遞歸線段樹 -- 單點修改,維護區間最大值
//Segment Tree
Node ST[maxn<<4];
int N;
void Build(int n){//初始化
N=1;while(N < n+2) N <<= 1;
int I=N <<1;
Node X=Node(0,0);
for(int i=0;i<I;++i) ST[i]=X;
}
void Update(int L,Node X){//點更新
for(int s=N+L;s;s>>=1){
if(ST[s]<X) ST[s]=X;
else break;
}
}
Node Query(int L,int R){//查詢區間最大值
Node ANS;
for(int s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1){
if(~s&1&&ANS < ST[s^1]) ANS=ST[s^1];
if( t&1&&ANS < ST[t^1]) ANS=ST[t^1];
}
return ANS;
}
//離散化
int Rank[maxn<<2],Rn;
void SortRank(){
int I=1;
sort(Rank+1,Rank+Rn+1);
for(int i=2;i<=Rn;++i) if(Rank[i]!=Rank[i-1]) Rank[++I]=Rank[i];
Rn=I;
}
int GetRank(int x){
int L=1,R=Rn;//[L,R] first >=x
while(L^R){
int M=(L+R)>>1;
if(Rank[M]<x) L=M+1;
else R=M;
}
return L;
}
//Line Information
struct Window{
int A,B,C;//[A,B)--C
Window(){}
Window(int A,int B,int C):A(A),B(B),C(C){}
}W[maxn];int Wn;
struct Ad{
int L,R;//[L,R)
Ad(){}
Ad(int L,int R):L(L),R(R){}
}A[maxn];int An;
//SweepLine
struct SweepLine{
int X,ID;
bool TYPE,IN;//type1=Ad type0=Window
SweepLine(){}
SweepLine(int X,int ID,int TYPE,int IN):X(X),ID(ID),TYPE(TYPE),IN(IN){}
bool operator <(const SweepLine &B)const{return X < B.X || X==B.X && TYPE < B.TYPE;}
}Line[maxn<<2];int Ln;
int n,m;
int main(void)
{
while(~scanf("%d%d",&n,&m)){
Ln=Rn=An=Wn=0;
for(int i=1;i<=n;++i){
int l,r;scanf("%d%d",&l,&r);
Rank[++Rn]=l;
Rank[++Rn]=r;
A[++An]=Ad(l,r);
}
for(int i=1;i<=m;++i){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
Rank[++Rn]=a;
Rank[++Rn]=b;
W[++Wn]=Window(a,b,c);
}
//離散化
SortRank();
//掃描線
for(int i=1;i<=n;++i){
Line[Ln++]=SweepLine(GetRank(A[i].L),i,1,1);
Line[Ln++]=SweepLine(GetRank(A[i].R),i,1,0);
}
for(int i=1;i<=m;++i){
Line[Ln++]=SweepLine(GetRank(W[i].A),i,0,1);
Line[Ln++]=SweepLine(GetRank(W[i].B),i,0,0);
}
sort(Line,Line+Ln);
L[0]=R[0]=S[0]=IP=TA=TB=0;//SBT初始化
Build(Rn);//線段樹初始化
LL ANS=0;int I=0,AID,WID;
for(int i=1;i<=Rn;++i){
while(I < Ln && Line[I].X == i){
if(Line[I].TYPE){//更新
if(Line[I].IN){//L操作
Insert(TB,Node(A[Line[I].ID].L,Line[I].ID));
Insert(TA,Node(A[Line[I].ID].R,Line[I].ID));
}
else{//R操作
Delete(TB,Node(A[Line[I].ID].L,Line[I].ID));
Delete(TA,Node(A[Line[I].ID].R,Line[I].ID));
Update(GetRank(A[Line[I].ID].L),Node(A[Line[I].ID].R-A[Line[I].ID].L,Line[I].ID));
}
}
else{//計算
if(Line[I].IN){//A操作
int rt=Max(TA);
if(rt){
LL D=((LL) W[Line[I].ID].C)*(min(W[Line[I].ID].B,K[rt].V)-W[Line[I].ID].A);
if(D > ANS){
ANS=D;
WID=Line[I].ID;
AID=K[rt].ID;
}
}
}
else{//B操作
int rt=Min(TB);
if(rt){
LL D=((LL) W[Line[I].ID].C)*(W[Line[I].ID].B-max(W[Line[I].ID].A,K[rt].V));
if(D > ANS){
ANS=D;
WID=Line[I].ID;
AID=K[rt].ID;
}
}
Node X=Query(GetRank(W[Line[I].ID].A),GetRank(W[Line[I].ID].B));
if(X.V){
LL D=((LL) W[Line[I].ID].C)*X.V;
if(D > ANS){
ANS=D;
WID=Line[I].ID;
AID=X.ID;
}
}
}
}
++I;
}
}
printf("%I64d\n",ANS);
if(ANS) printf("%d %d\n",AID,WID);
}
return 0;
}
另一份代碼:(561ms 41500KB)
/*
561 ms 41500 KB
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define out(i) <<#i<<"="<<(i)<<" "
#define OUT1(a1) cout out(a1) <<endl
#define OUT2(a1,a2) cout out(a1) out(a2) <<endl
#define OUT3(a1,a2,a3) cout out(a1) out(a2) out(a3)<<endl
#define maxn 200007
#define LL long long
using namespace std;
//非遞歸線段樹 -- 單點修改,維護區間最大值
//Segment Tree
struct Node{
int V,ID;
Node():V(0),ID(0){}
Node(int V,int ID):V(V),ID(ID){}
bool operator <(const Node &B)const{return V < B.V || V==B.V && ID < B.ID;}
bool operator ==(const Node &B)const{return V==B.V&&ID==B.ID;}
}ST[maxn<<4];
int N;
void Build(int n){
N=1;while(N < n+2) N <<= 1;
int I=N <<1;
Node X=Node(0,0);
for(int i=0;i<I;++i) ST[i]=X;
}
void Update(int L,Node X){//點更新
for(int s=N+L;s;s>>=1){
if(ST[s]<X) ST[s]=X;
else break;
}
}
Node Query(int L,int R){//查詢區間最大值
Node ANS;
for(int s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1){
if(~s&1&&ANS < ST[s^1]) ANS=ST[s^1];
if( t&1&&ANS < ST[t^1]) ANS=ST[t^1];
}
return ANS;
}
//離散化
int Rank[maxn<<2],Rn;
void SortRank(){
int I=1;
sort(Rank+1,Rank+Rn+1);
for(int i=2;i<=Rn;++i) if(Rank[i]!=Rank[i-1]) Rank[++I]=Rank[i];
Rn=I;
}
int GetRank(int x){
int L=1,R=Rn;//[L,R] first >=x
while(L^R){
int M=(L+R)>>1;
if(Rank[M]<x) L=M+1;
else R=M;
}
return L;
}
//Information
struct Window{
int A,B,C;//[A,B)--C
Window(){}
Window(int A,int B,int C):A(A),B(B),C(C){}
}W[maxn];int Wn;
struct Ad{
int L,R;//[L,R)
Ad(){}
Ad(int L,int R):L(L),R(R){}
}A[maxn];int An;
//SweepLine
struct SweepLine{
int X,ID;
bool TYPE,IN;//type1=Ad type0=Window
SweepLine(){}
SweepLine(int X,int ID,int TYPE,int IN):X(X),ID(ID),TYPE(TYPE),IN(IN){}
bool operator <(const SweepLine &B)const{return X < B.X || X==B.X && TYPE < B.TYPE;}
}Line[maxn<<2];int Ln;
int n,m;
int main(void)
{
while(~scanf("%d%d",&n,&m)){
Ln=Rn=An=Wn=0;
for(int i=1;i<=n;++i){
int l,r;scanf("%d%d",&l,&r);
Rank[++Rn]=l;
Rank[++Rn]=r;
A[++An]=Ad(l,r);
}
for(int i=1;i<=m;++i){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
Rank[++Rn]=a;
Rank[++Rn]=b;
W[++Wn]=Window(a,b,c);
}
//離散化
SortRank();
//掃描線
for(int i=1;i<=n;++i){
Line[Ln++]=SweepLine(GetRank(A[i].L),i,1,1);
Line[Ln++]=SweepLine(GetRank(A[i].R),i,1,0);
}
for(int i=1;i<=m;++i){
Line[Ln++]=SweepLine(GetRank(W[i].A),i,0,1);
Line[Ln++]=SweepLine(GetRank(W[i].B),i,0,0);
}
sort(Line,Line+Ln);
Build(Rn);//線段樹初始化
LL ANS=0;int I=0,AID,WID;
int RM=0,RID=0;
for(int i=1;i<=Rn;++i){//第一次掃描線,從左到右,計算第一,二類情況
if(Rank[i]>RM) RM=Rank[i],RID=0;
while(I < Ln && Line[I].X == i){
if(Line[I].TYPE){//更新
if(Line[I].IN){//L操作
if(A[Line[I].ID].R>RM) RM=A[Line[I].ID].R,RID=Line[I].ID;
}
else{//R操作
Update(GetRank(A[Line[I].ID].L),Node(A[Line[I].ID].R-A[Line[I].ID].L,Line[I].ID));
}
}
else{//計算
if(Line[I].IN){//A操作
if(RID){
LL D=((LL) W[Line[I].ID].C)*(min(RM,W[Line[I].ID].B)-W[Line[I].ID].A);
if(D > ANS){
ANS=D;
WID=Line[I].ID;
AID=RID;
}
}
}
else{//B操作
Node X=Query(GetRank(W[Line[I].ID].A),GetRank(W[Line[I].ID].B));
if(X.V){
LL D=((LL) W[Line[I].ID].C)*X.V;
if(D > ANS){
ANS=D;
WID=Line[I].ID;
AID=X.ID;
}
}
}
}
++I;
}
}
I=Ln-1;int LM=Rank[Rn],LID=0;
for(int i=Rn;i>=1;--i){//第二次掃描線,從右往左,計算第三類情況
if(Rank[i]<LM) LM=Rank[i],LID=0;
while(I>=0 && Line[I].X == i){
if(Line[I].TYPE){//更新
if(!Line[I].IN) {//R操作
if(A[Line[I].ID].L <LM) LM=A[Line[I].ID].L,LID=Line[I].ID;
}
}
else{//計算
if(!Line[I].IN){//B操作
if(LID){
LL D=((LL) W[Line[I].ID].C)*(W[Line[I].ID].B-max(W[Line[I].ID].A,LM));
if(D > ANS){
ANS=D;
WID=Line[I].ID;
AID=LID;
}
}
}
}
--I;
}
}
printf("%I64d\n",ANS);
if(ANS) printf("%d %d\n",AID,WID);
}
return 0;
}
將所有線段都加入掃描線,每條線段都出現兩次:一次入邊, 一次出邊。
以下就用L,R代表無權值線段的入邊,出邊。用A,B代表有權值線段的入邊,出邊。
掃描線的過程,就是將所有的A,B,L,R排序,然後從小到大掃描一遍。
那麼,整個過程實際上,就是在A,B,L,R這四個端點各需要進行什麼操作的問題。