Sign on Fence CodeForces - 484E
http://codeforces.com/problemset/problem/484/E
题意:
给定一个长度为n的数列,有m次询问,询问形如l r k
要你在区间[l,r]内选一个长度为k的区间,求区间最小数的最大值
整体二分啊。。。。O((n+m)log(n)log(值域))
对于一个询问,可以二分答案后(设二分出的答案为ans),将>=ans的数标记为1,<ans的数标记为0,然后求该询问区间内最长的连续且全为1的一段的长度x,如果x>=该询问要求的k则合法,否则不合法;
多次询问,发现可以整体二分
(如果带修改,貌似要可持久化一下整体二分中用的线段树?log^3??)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 struct Q 6 { 7 int type; 8 int l,r,x,num; 9 int pos,d; 10 }q[200100],qt1[200100],qt2[200100]; 11 int n,m,ans[100100],len; 12 //二分答案后,将>=mid+1的数改为1,<mid+1的数改为0,判定区间最长连续1的长度是否>=k 13 namespace SegT 14 { 15 struct Info 16 { 17 int ld,rd,xd,sz; 18 bool a1; 19 }dat[400100]; 20 Info operator+(const Info &a,const Info &b) 21 { 22 Info c; 23 c.a1=a.a1&&b.a1;c.sz=a.sz+b.sz; 24 c.ld=a.a1?a.sz+b.ld:a.ld;c.rd=b.a1?b.sz+a.rd:b.rd; 25 c.xd=max(a.rd+b.ld,max(a.xd,b.xd)); 26 return c; 27 } 28 Info nn[]={{0,0,0,1,0},{1,1,1,1,1}}; 29 int L,R,x; 30 #define mid (l+((r-l)>>1)) 31 #define lc (num<<1) 32 #define rc (num<<1|1) 33 void build(int l,int r,int num) 34 { 35 if(l==r) {dat[num]=nn[0];return;} 36 build(l,mid,lc);build(mid+1,r,rc); 37 dat[num]=dat[lc]+dat[rc]; 38 } 39 void _setx(int l,int r,int num) 40 { 41 if(l==r) {dat[num]=nn[x];return;} 42 if(L<=mid) _setx(l,mid,lc); 43 else _setx(mid+1,r,rc); 44 dat[num]=dat[lc]+dat[rc]; 45 } 46 Info _query(int l,int r,int num) 47 { 48 if(L<=l&&r<=R) return dat[num]; 49 if(L<=mid&&mid<R) return _query(l,mid,lc)+_query(mid+1,r,rc); 50 if(L<=mid) return _query(l,mid,lc); 51 if(mid<R) return _query(mid+1,r,rc); 52 exit(-1); 53 } 54 void setx(int l,int d) 55 { 56 L=l;x=d;_setx(1,n,1); 57 } 58 int query(int l,int r) 59 { 60 L=l;R=r;return _query(1,n,1).xd; 61 } 62 #undef mid 63 #undef lc 64 #undef rc 65 } 66 void solve(int lp,int rp,int l,int r) 67 { 68 if(lp>rp) return; 69 int i; 70 if(l==r) 71 { 72 for(i=lp;i<=rp;i++) 73 if(q[i].type==2) 74 ans[q[i].num]=l; 75 return; 76 } 77 int mid=l+((r-l)>>1),tlen1=0,tlen2=0;//qt1-->l,mid;qt2-->mid+1,r 78 for(i=lp;i<=rp;i++) 79 { 80 if(q[i].type==1) 81 { 82 if(q[i].d>mid) 83 SegT::setx(q[i].pos,1),qt2[++tlen2]=q[i]; 84 else 85 qt1[++tlen1]=q[i]; 86 } 87 else 88 { 89 if(SegT::query(q[i].l,q[i].r)>=q[i].x) qt2[++tlen2]=q[i]; 90 else qt1[++tlen1]=q[i]; 91 } 92 } 93 memcpy(q+lp,qt1+1,sizeof(Q)*tlen1); 94 memcpy(q+lp+tlen1,qt2+1,sizeof(Q)*tlen2); 95 solve(lp,lp+tlen1-1,l,mid); 96 for(i=lp+tlen1;i<=rp;i++) 97 if(q[i].type==1) 98 SegT::setx(q[i].pos,0); 99 solve(lp+tlen1,rp,mid+1,r); 100 } 101 int main() 102 { 103 int i,t; 104 scanf("%d",&n); 105 for(i=1;i<=n;i++) scanf("%d",&t),q[++len].type=1,q[len].pos=i,q[len].d=t; 106 scanf("%d",&m); 107 for(i=1;i<=m;i++) q[++len].type=2,scanf("%d%d%d",&q[len].l,&q[len].r,&q[len].x),q[len].num=i; 108 SegT::build(1,n,1); 109 solve(1,len,0,1e9); 110 for(i=1;i<=m;i++) printf("%d\n",ans[i]); 111 return 0; 112 }
貌似由于此题没有修改,可持久化线段树就是一个log了23333还是两个log,询问还是要二分答案的;可持久化线段树的做法大概是先离散化,然后将所有位置的值从小到大排序,建n棵线段树(当然共用了很多节点),第i棵线段树的第j位表示a[j]是(1)否(0)大于等于(所有n个数中)第i小的数(建法...应该是好建的,先不管了);查询就二分答案(设二分出的答案为ans,注意离散化),在第ans棵线段树上对应区间查询最长连续全为1段的长度,判合法;