题意
给出一个n排列,m次询问,每次查询长度最小的区间,使得这个区间排序后出现连续的整数(即相邻的差为1),并且包含(或等于)区间[l,r]。若长度相等,取l端点最小的。
n,m<=1E5
思考
重要的性质:
1.一个区间是连续的,当且仅当其存在r-l个无序二元组(x,y),满足|x-y|=1。
2.若两个连续区间有交,其交必然是连续的。因为交的部分要同时满足左右部分是连续的。若交不连续,至多满足一个部分连续。
根据性质2,可以知道对于某个询问,可以通过不断移动要求的右端点,直到出现的所有的连续区间至少有一个完全覆盖了它。此时长度最小的一定是最优的。
根据性质1,可以先离线,再依次将数组中的每个数产生的贡献加入线段树。具体地讲,对于数ai,若ai-1,ai+1出现的位置在i左侧,则在区间[1,位置ai-1],[1,位置ai+1]加1,表示多出了一个差为1的无序二元组。那只要知道哪些[l,i]满足vall+l=r即可(r-l=vall)。方便起见,最开始建树时vali=i,则只要查询vall=i。
由于一个区间满足条件的无序二元组最多为r-l个,维护最大值及其位置即可。
接下来是询问部分。离线之后,按r排序,每次加入r=i的询问至set(实际是multiset,因为有重)中。set中按l从大到小排序。这样一来,每次查询尽可能大的左端点,一旦无法覆盖就能break。
复杂度O(nlogn+mlogm)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1E5+5; 4 int n,m,a[maxn],ansX[maxn],where[maxn],ansY[maxn]; 5 int t[maxn*4],tag[maxn*4],maxVal[maxn*4],maxPos[maxn*4]; 6 struct query 7 { 8 int x,y,id; 9 bool operator < (query A)const 10 { 11 if(x!=A.x) 12 return x>A.x; 13 return y<A.y; 14 } 15 }q[maxn]; 16 vector<query>wait[maxn]; 17 void pushdown(int l,int r,int num) 18 { 19 if(!tag[num]) 20 return; 21 maxVal[num]+=tag[num]; 22 if(l==r) 23 { 24 tag[num]=0; 25 return; 26 } 27 tag[num<<1]+=tag[num]; 28 tag[num<<1|1]+=tag[num]; 29 tag[num]=0; 30 } 31 void update(int l,int r,int num) 32 { 33 pushdown(l,r,num); 34 if(l==r) 35 return; 36 int mid=(l+r)>>1; 37 pushdown(l,mid,num<<1); 38 pushdown(mid+1,r,num<<1|1); 39 if(maxVal[num<<1]<=maxVal[num<<1|1]) 40 { 41 maxVal[num]=maxVal[num<<1|1]; 42 maxPos[num]=maxPos[num<<1|1]; 43 } 44 else 45 { 46 maxVal[num]=maxVal[num<<1]; 47 maxPos[num]=maxPos[num<<1]; 48 } 49 } 50 void build(int l,int r,int num) 51 { 52 if(l==r) 53 { 54 maxVal[num]=l; 55 maxPos[num]=l; 56 return; 57 } 58 int mid=(l+r)>>1; 59 build(l,mid,num<<1); 60 build(mid+1,r,num<<1|1); 61 update(l,r,num); 62 } 63 void add(int L,int R,int l,int r,int x,int num) 64 { 65 update(l,r,num); 66 if(L<=l&&r<=R) 67 { 68 tag[num]+=x; 69 update(l,r,num); 70 return; 71 } 72 if(r<L||R<l) 73 return; 74 int mid=(l+r)>>1; 75 add(L,R,l,mid,x,num<<1); 76 add(L,R,mid+1,r,x,num<<1|1); 77 update(l,r,num); 78 } 79 int ask(int L,int R,int l,int r,int x,int num) 80 { 81 update(l,r,num); 82 if(L<=l&&r<=R) 83 { 84 if(maxVal[num]!=x) 85 return -1; 86 return maxPos[num]; 87 } 88 if(r<L||R<l) 89 return -1; 90 int mid=(l+r)>>1; 91 return max(ask(L,R,l,mid,x,num<<1),ask(L,R,mid+1,r,x,num<<1|1)); 92 } 93 void out(int l,int r,int num) 94 { 95 update(l,r,num); 96 if(l==r) 97 { 98 cout<<maxVal[num]<<' '; 99 return; 100 } 101 int mid=(l+r)>>1; 102 out(l,mid,num<<1); 103 out(mid+1,r,num<<1|1); 104 } 105 int main() 106 { 107 ios::sync_with_stdio(false); 108 cin>>n; 109 build(1,n,1); 110 for(int i=1;i<=n;++i) 111 { 112 cin>>a[i]; 113 where[a[i]]=i; 114 } 115 cin>>m; 116 for(int i=1;i<=m;++i) 117 { 118 cin>>q[i].x>>q[i].y; 119 q[i].id=i; 120 wait[q[i].y].push_back(q[i]); 121 } 122 where[n+1]=where[0]=n+1; 123 multiset<query>S; 124 for(int i=1;i<=n;++i) 125 { 126 if(where[a[i]-1]<i) 127 add(1,where[a[i]-1],1,n,1,1); 128 if(where[a[i]+1]<i) 129 add(1,where[a[i]+1],1,n,1,1); 130 for(int j=0;j<wait[i].size();++j) 131 { 132 // cout<<"NEW"<<wait[i][j].x<<' '<<wait[i][j].y<<endl; 133 S.insert(wait[i][j]); 134 } 135 while(!S.empty()) 136 { 137 set<query>::iterator pt=S.begin(); 138 // cout<<(*pt).x<<' '<<(*pt).y<<" "<<S.size()<<"---"<<endl; 139 int pos=ask(1,(*pt).x,1,n,i,1); 140 if(pos==-1) 141 break; 142 ansX[(*pt).id]=pos; 143 ansY[(*pt).id]=i; 144 S.erase(pt); 145 } 146 } 147 for(int i=1;i<=m;++i) 148 cout<<ansX[i]<<" "<<ansY[i]<<endl; 149 return 0; 150 }