poj线段树专题

来源:互联网 发布:手机网络相关参数含义 编辑:程序博客网 时间:2024/05/14 12:12

 

 

poj 2528 水题

#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define N 40500int n;struct node{int l,r,flag;}tree[N*4];struct Inter{int l,r;}inter[N];int vis[N];int max(int aa,int bb){return aa>bb?aa:bb;}void build(int pos,int l,int r){tree[pos].l=l;tree[pos].r=r;tree[pos].flag=0;if(l==r)return;int mid=(l+r)>>1;build(pos*2,l,mid);build(pos*2+1,mid+1,r);}void update(int pos,int l,int r,int val){if(l<=tree[pos].l&&tree[pos].r<=r){tree[pos].flag=val;return;}int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid)update(pos*2,l,r,val);else if(l>mid)update(pos*2+1,l,r,val);else{update(pos*2,l,mid,val);update(pos*2+1,mid+1,r,val);}}int query(int pos,int x){if(tree[pos].l==tree[pos].r)return tree[pos].flag;int mid=(tree[pos].l+tree[pos].r)>>1;if(x<=mid)return max(tree[pos].flag,query(pos*2,x));else if(x>mid)return max(tree[pos].flag,query(pos*2+1,x));}struct point{int idx,val;}tt[N*2];bool cmp(point left,point right){return left.val<right.val;}int main (){int test;scanf("%d",&test);while(test--){memset(vis,0,sizeof(vis));scanf("%d",&n);for(int i=1;i<=n;++i){scanf("%d%d",&inter[i].l,&inter[i].r);tt[2*i-1].val=inter[i].l;tt[2*i-1].idx=i;tt[2*i].val=inter[i].r;tt[2*i].idx=i;}sort(tt+1,tt+1+2*n,cmp);int pre=tt[1].val-1;int cnt=0;for(int i=1;i<=2*n;++i){if(pre!=tt[i].val){pre=tt[i].val;tt[i].val=++cnt;}else tt[i].val=cnt;if(pre==inter[tt[i].idx].l)inter[tt[i].idx].l=cnt;if(pre==inter[tt[i].idx].r)inter[tt[i].idx].r=cnt;}build(1,1,cnt);for(int i=1;i<=n;++i)update(1,inter[i].l,inter[i].r,i);int temp;int ans=0;vis[0]=1;for(int i=1;i<=cnt;++i){temp=query(1,i);if(!vis[temp])ans++;vis[temp]=1;}printf("%d\n",ans);}return 0;}


poj 2828

一开始还天真的用vector模拟,事实证明vector真是慢,insert不是o(1)的

此题成功之处是看不出事线段树

一个人排在什么位置和插入的位置和顺序有关,在插入的过程中是动态变化的

所以从最后排的人扫回去,他排在pos后面,相当于询问某个位置前面有多少个空位
 

#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define N 250000int n;struct node{int l,r,val;}tree[N*4];struct point{int pos,val;}p[N];void build(int pos,int l,int r){tree[pos].l=l;tree[pos].r=r;tree[pos].val=0;if(l==r)return ;int mid=(l+r)>>1;build(pos*2,l,mid);build(pos*2+1,mid+1,r);}void update(int pos,int x,int val){if(tree[pos].l==tree[pos].r){tree[pos].val++;return;}tree[pos].val++;int mid=(tree[pos].l+tree[pos].r)>>1;if(x<=mid)update(pos*2,x,val);else update(pos*2+1,x,val);}int query(int pos,int l,int r){if(l<=tree[pos].l&&tree[pos].r<=r)return tree[pos].val;int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid)return query(pos*2,l,r);else if(l>mid)return query(pos*2+1,l,r);else return query(pos*2,l,mid)+query(pos*2+1,mid+1,r);}int ans[N];int main (){while(scanf("%d",&n)!=EOF){build(1,1,n);for(int i=1;i<=n;++i)scanf("%d%d",&p[i].pos,&p[i].val);int l,r,mid,temp;for(int i=n;i>=1;--i){l=1;r=n;while(l<r){mid=(l+r)>>1;temp=query(1,1,mid);if(mid-temp<p[i].pos+1)l=mid+1;else r=mid;}ans[l]=p[i].val;update(1,l,1);}for(int i=1;i<=n;++i){printf("%d",ans[i]);if(i<n)printf(" ");else printf("\n");}}return 0;}

 

 

poj 2777

1.关于lazy的优化:当刚好能够覆盖节点表示的区间,就不再往下走,并做标记;对于途中经过的点,先把节点的状态push down 给子节点再往下走,

子节点update完成后,再push up 把子节点的状态反馈给自己。询问时遇到有标记被整个区间覆盖的节点就不再往下走。

这能够避免每次更新或者询问总是几乎遍历整棵树

2.用一个32位整数表示状态,每个节点储存包含哪些颜色

 

#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define N 105000int L,T,O;struct node{int l,r,flag;int color;}tree[N*4];void build(int pos,int l,int r){tree[pos].l=l;tree[pos].r=r;tree[pos].flag=1;tree[pos].color=1;if(l==r)return;int mid=(l+r)>>1;build(pos*2,l,mid);build(pos*2+1,mid+1,r);}void update(int pos,int l,int r,int val){if(l<=tree[pos].l&&tree[pos].r<=r){tree[pos].flag=1;tree[pos].color=(1<<(val-1));return;}if(tree[pos].flag)  // push down{tree[pos*2].color=tree[pos].color;tree[pos*2+1].color=tree[pos].color;tree[pos*2].flag=tree[pos*2+1].flag=1;tree[pos].flag=0;}int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid)update(pos*2,l,r,val);else if(l>mid)update(pos*2+1,l,r,val);else{update(pos*2,l,mid,val);update(pos*2+1,mid+1,r,val);}tree[pos].flag=0;tree[pos].color=tree[pos*2].color|tree[pos*2+1].color;  // push up}int query(int pos,int l,int r){if(l<=tree[pos].l&&tree[pos].r<=r||tree[pos].flag)return tree[pos].color;int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid)return query(pos*2,l,r);else if(l>mid)return query(pos*2+1,l,r);else return query(pos*2,l,mid)|query(pos*2+1,mid+1,r);}int decode(int x){int ans=0;while(x){if(x&1)ans++;x>>=1;}return ans;}int main (){scanf("%d%d%d",&L,&T,&O);char ch;int a,b,c;build(1,1,L);while(O--){scanf(" %c",&ch);if(ch=='P'){scanf("%d%d",&a,&b);if(b<a)swap(a,b);int t=query(1,a,b);int ans=decode(t);printf("%d\n",ans);}else{scanf("%d%d%d",&a,&b,&c);if(b<a)swap(a,b);update(1,a,b,c);}}return 0;}


 

 poj 2886

线段树的另一功能:获得一个未出列的人中的排位对应的下标!!

应用反素数,先把表打出来,不要放进里面算

之前以为用欧拉函数,把不与n互质的数的个数 和 小于n的数中能整除n的数的个数 混淆

#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <algorithm>using namespace std;#define N 550500int phi[N+49],n,k;int card[N];char str[N][15];int RPrime[]={//反素数      1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,      20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,      554400  };    int fact[]={//反素数约数个数      1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,100,108,120,128,      144,160,168,180,192,200,216  };  struct node{int l,r,sum;}tree[N*4];void build(int pos,int l,int r){tree[pos].l=l;tree[pos].r=r;if(l==r){tree[pos].sum=1;return;}int mid=(l+r)>>1;build(pos*2,l,mid);build(pos*2+1,mid+1,r);tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum;}int luck;void update(int pos,int idx){tree[pos].sum--;if(tree[pos].l==tree[pos].r){luck=tree[pos].l;return;}if(idx>tree[pos*2].sum)update(pos*2+1,idx-tree[pos*2].sum);else update(pos*2,idx);}int main (){while(scanf("%d%d",&n,&k)!=EOF){for(int i=1;i<=n;++i)scanf("%s%d",str[i],&card[i]);int ma=0,high=fact[ma];for(int i=0;i<=36&&RPrime[i]<=n;++i)if(fact[ma]<fact[i])ma=i,high=RPrime[i];build(1,1,n);int r=1;luck=0;for(int i=1;i<=high;++i){int mod=tree[1].sum;if(k>0)r=(r-2+k%mod+mod)%mod+1;else r=(r-1+k%mod+mod)%mod+1;update(1,r);k=card[luck];//printf("%s\n",str[luck]);//printf("k=%d  r=%d   luck=%d\n",k,r,luck);}printf("%s %d\n",str[luck],fact[ma]);}return 0;}


 

 poj 2750

经典的单点更新,求区间最值,没有想到怎么lazy

现在想想倒不用lazy,lazy是区间更新时避免更新很多个单点而变成超时的,现在是单点,无压力

tree[pos].max=max(tree[pos*2].max,tree[pos*2+1].max,tree[pos*2].rmax+tree[pos*2+1].lmax)

接下来就是3个问题:

1.环:转化为求连续子区间的最大值或者总和减去连续子区间的最小值

2.答案的区间不能使整个区间,只要在更新时加一句判断就可以了

3.特判:所有数都是负的的特殊情况

 

#include <iostream>#include <cstring>#include <cstdio>#include <cstdlib>using namespace std;#define N 105000struct node{int l,r,lmax,lmin,rmax,rmin,max,min,sum,flag;}tree[N*4];int a[N*2],n,m;inline int max(int aa,int bb){return aa>bb?aa:bb;}inline int min(int aa,int bb){return aa<bb?aa:bb;}void push_up(int pos){tree[pos].flag=(tree[pos*2].flag&&tree[pos*2+1].flag);tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum;tree[pos].lmax=max(tree[pos*2].lmax,tree[pos*2].sum+tree[pos*2+1].lmax);tree[pos].rmax=max(tree[pos*2+1].rmax,tree[pos*2+1].sum+tree[pos*2].rmax);tree[pos].max=max(tree[pos*2].max,tree[pos*2+1].max);if(tree[pos*2].rmax+tree[pos*2+1].lmax!=tree[1].sum)tree[pos].max=max(tree[pos].max,tree[pos*2].rmax+tree[pos*2+1].lmax);tree[pos].lmin=min(tree[pos*2].lmin,tree[pos*2].sum+tree[pos*2+1].lmin);tree[pos].rmin=min(tree[pos*2+1].rmin,tree[pos*2+1].sum+tree[pos*2].rmin);tree[pos].min=min(min(tree[pos*2].min,tree[pos*2+1].min),tree[pos*2].rmin+tree[pos*2+1].lmin);}void build(int pos,int l,int r){tree[pos].l=l;tree[pos].r=r;if(l==r){tree[pos].max=tree[pos].min=tree[pos].sum=tree[pos].lmax=tree[pos].lmin=tree[pos].rmax=tree[pos].rmin=a[l];if(a[l]<0) tree[pos].flag=1;return;}int mid=(l+r)>>1;build(pos*2,l,mid);build(pos*2+1,mid+1,r);push_up(pos);}void update(int pos,int idx,int val){if(tree[pos].l==tree[pos].r){tree[pos].lmax=tree[pos].rmax=tree[pos].lmin=tree[pos].rmin=tree[pos].sum=val;tree[pos].max=tree[pos].min=val;if(val<0) tree[pos].flag=1;return;}int mid=(tree[pos].l+tree[pos].r)>>1;if(idx<=mid)update(pos*2,idx,val);else update(pos*2+1,idx,val);push_up(pos);}int main (){while(scanf("%d",&n)!=EOF){for(int i=1;i<=n;++i)scanf("%d",&a[i]);build(1,1,n);scanf("%d",&m);int A,B;int res;while(m--){scanf("%d%d",&A,&B);update(1,A,B);res=tree[1].max;if(!tree[1].flag)res=max(res,tree[1].sum-tree[1].min);printf("%d\n",res);}}return 0;}


 

poj 2482

1.二维线段树,二维限制,先排序,边插入便询问

2.离散化

3 把询问当做点放进里面排序

(gmj总结的常考点基本在这题都出来了)

这里有个trick,左下角的点权值为c,右上角的权值设为-c,这样在超过矩形的范围后由于区间加上-c,c的影响被消除,所以这时询问不到c的影响

关于线段树的区间更新,区间询问之前一直不太理解:

每个节点记录已加值(加在节点表示的整个区间)和待加值(加在节点表示区间的部分区间)

每次更新往下跑,直到目标区间与节点表示区间一样,加在已加值上,表示对整段的更新

注意点在矩形的左下角或者右上角的时候不一定是答案,所以开始我用树状数组水不过= =,比如

2 3 3 

3 1 1

1 3 1

就过不了

其他的就是考验边界的处理了



#include <iostream>#include <cstring>#include <cstdio>#include <cstdlib>#include <algorithm>using namespace std;#define N 35500#define ll long longint n,w,h;struct node{ll x,y;int flag,c,id;}p[N];struct Inter{int x0,y0,x1,y1;}inter[N];int ly[N],lx[N];struct pnode{int l,r,val,max;}tree[N*4];int max(int aa,int bb){return aa>bb?aa:bb;}void build(int pos,int l,int r){tree[pos].l=l;tree[pos].r=r;tree[pos].val=tree[pos].max=0;if(l==r)return;int mid=(l+r)>>1;build(pos*2,l,mid);build(pos*2+1,mid+1,r);}void update(int pos,int l,int r,int v){if(l<=tree[pos].l&&tree[pos].r<=r)  // 不再往下跑,表示整段更新{tree[pos].max+=v;tree[pos].val+=v;return;}//tree[pos].idx+=v;  加在idx上的区间可能不相交,错!int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid)update(pos*2,l,r,v);else if(l>mid)update(pos*2+1,l,r,v);else{update(pos*2,l,mid,v);update(pos*2+1,mid+1,r,v);}tree[pos].max=max(tree[pos*2].max,tree[pos*2+1].max)+tree[pos].val;}bool cmpx(int left,int right){return p[left].x<p[right].x;}bool cmpy(int left,int right){return p[left].y<p[right].y;}bool cmp(node left,node right){if(left.x==right.x){if(left.y==right.y)return left.flag>right.flag; // 先插入再询问return left.y<right.y;}return left.x<right.x;}int main (){while(scanf("%d%d%d",&n,&w,&h)!=EOF){for(int i=1;i<=n;++i){scanf("%lld%lld%d",&p[2*i-1].x,&p[2*i-1].y,&p[2*i-1].c);p[2*i-1].flag=1;p[2*i-1].id=i;p[2*i].x=p[2*i-1].x+w-1;p[2*i].y=p[2*i-1].y+h-1;p[2*i].c=-p[2*i-1].c;p[2*i].flag=0;p[2*i].id=i;}sort(p+1,p+1+2*n,cmp);for(int i=1;i<=2*n;++i)lx[i]=ly[i]=i;sort(lx+1,lx+1+2*n,cmpx);sort(ly+1,ly+1+2*n,cmpy);ll pre=p[lx[1]].x-2;int cnt=1;for(int i=1;i<=2*n;++i)if(pre!=p[lx[i]].x)pre=p[lx[i]].x,p[lx[i]].x=++cnt;else p[lx[i]].x=cnt;pre=p[ly[1]].y-2;cnt=1;for(int i=1;i<=2*n;++i)if(pre!=p[ly[i]].y)pre=p[ly[i]].y,p[ly[i]].y=++cnt;else p[ly[i]].y=cnt;for(int i=1;i<=2*n;++i)if(p[i].flag)inter[p[i].id].x0=p[i].x,inter[p[i].id].y0=p[i].y;else inter[p[i].id].x1=p[i].x,inter[p[i].id].y1=p[i].y;build(1,1,p[ly[2*n]].y);int ans=-1;for(int i=1;i<=2*n;++i){if(ans<tree[1].max)ans=tree[1].max;update(1,inter[p[i].id].y0,inter[p[i].id].y1,p[i].c);}printf("%d\n",ans);}return 0;}


原创粉丝点击