poj 2528 水题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 40500
int 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 250000
int 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 105000
int 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 550500
int 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 105000
struct 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 long
int 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;
}