首行用来ym hh大牛..Orz
题目单:http://www.notonlysuccess.com/index.php/segment-tree-complete/
-----------------------------------------------------------------------------------------------
初识线段树 模板题:
hdu1166 敌兵布阵 , hdu1754 I hate It (求区间和).
Tips:
静态线段树内有n个节点通常要把内存开到n*4,才能保证不re.
具体见http://chuanwang66.iteye.com/blog/1428598
初级应用:
hdu1394 Minimum Inversion Number.
求逆序数的方法: 按照数列的顺序每次访问一个数字x查找区间[1~x)已经插入的
数字的个数,即为逆序数,然后把x插入.而注意到数列第1项为a的话,与a有关的逆序数
x[a]=a-1,于是可以利用这个性质每次循环移位后得到新的逆序数:
inver=inver-x[a]+n-1-x[a].
hdu2795 Billboard.
注意到最小的变量n只有20w,而高度h远大于它,显然h>n的部分是不用考虑的.
uvalive5798 Jupiter Atacks.
题目大意是给一个b进制,l位数的数字,动态改变一些位的值,询问区间[l,r]的值并模p
例如对10进制的 12345 询问[2,5] 得到一个值2345 输出它模p的值.
与区间和类似,不同的是得到左区间的l_sum和右区间的r_sum,合并答案时res=l_sum*t+r_sum,而不是直接相加.
这里的t与进制和右区间长度有关.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<stdio.h>
#include<string.h>
#define maxn 100001
typedef long long LL;
LL sum[maxn*4];
long b,p,len,n;
LL pow(LL cur,long times)
{
LL res;
if(times==1)return cur;
res=pow(cur,times>>1);
res=res*res;
if(res>=p)res%=p;
if(times&1)res=res*cur;
if(res>=p)res%=p;
return res;
}
void update(long goal,long turn,long l,long r,long cur)
{
long mid=(l+r)>>1,Len;
LL tmp;
if(l==r)
{
sum[cur]=turn;
if(sum[cur]>=p)sum[cur]%=p;
return;
}
if(goal>mid)
update(goal,turn,mid+1,r,cur<<1|1);
else
update(goal,turn,l,mid,cur<<1);
Len=r-mid;
tmp=pow(b,Len);
sum[cur]=sum[cur<<1]*tmp+sum[cur<<1|1];
if(sum[cur]>=p)sum[cur]%=p;
}
LL query(long L,long R,long l,long r,long cur)
{
long mid=(l+r)>>1,Len;
LL res1,res2,res,tmp;
if(L==l && r==R)return sum[cur];
if(R<=mid)
res=query(L,R,l,mid,cur<<1);
else if(mid<L)
res=query(L,R,mid+1,r,cur<<1|1);
else
{
res1=query(L,mid,l,mid,cur<<1);
res2=query(mid+1,R,mid+1,r,cur<<1|1);
Len=R-mid;
tmp=pow(b,Len);
res=res1*tmp+res2;
if(res>=p)res%=p;
}
return res;
}
int main()
{
long A,B;
char op[10];
freopen("test.txt","r",stdin);
while(scanf("%ld%ld%ld%ld",&b,&p,&len,&n))
{
if(b+p+len+n==0)return 0;
memset(sum,0,sizeof(sum));
while(n--)
{
scanf("%s%ld%ld",op,&A,&B);
if(op[0]=='E')
update(A,B,1,len,1);
if(op[0]=='H')
printf("%lld\n",query(A,B,1,len,1));
}
printf("-\n");
}
return 0;
}
成段更新:
对将要更新的区间的标号添加一个标记,当递归访问前才根据标记更新到儿子节点.
void PushDown(int cur) { if(lazy[cur]) { flag[cur<<1]=1; flag[cur<<1|1]=1; lazy[cur]=0; } }
特别注意PushDown要放在递归边界后,否则可能清除节点标记造成无限递归.
模板题: hdu 1698,poj 3468
离散化:
poj2528 Mayor's posters.
这题的离散化有个坑, 差>1的相邻两数离散化后中间的空隙就不能被表示出来.
poj3225 Help whith Intervals
需要把实数区间离散化,用整数表示,同样有个坑:
为了表示开闭区间要把原端点拆成2部分,n<<1表示端点n,(n<<1|1)表示[n+ξ ,n+1)
(ξ表示无穷小)
第2个坑:
只用一个数组col[cur]表示这个区间的染色情况:1 染色,0 未染色,-1 表示2种都有.
这样当col[cur]=-1,而又是面对C或S操作时,就要继续向下update,于是col的成段更新退化成了O(n)
导致我TLE 5次 T_T
第3个坑:
为防止区间更新退化成O(n),再用一个数组xor[cur],表示这个区间是否要被覆盖,每次更新时
如果col[cur]=-1,把xor[cur]^1就可以了.但是为了防止重复覆盖,PushDown后xor[cur]要清空
.....情况很多,详见代码.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<stdio.h> #include<string.h> #define maxn 65535 int cov[(maxn+1)<<3]; int xo[(maxn+1)<<3]; int flag; typedef struct node { int l; int r; }node;node now; void print_range(int l,int r) { if (l&1)printf("(%d,",l>>1); else printf("[%d,",l>>1); if (r&1)printf("%d)",(r+1)>>1); else printf("%d]",r>>1); } void FXOR(int cur) { if(cov[cur]!=-1)cov[cur]^=1; else xo[cur]^=1; } void PushDown(int cur) { if(cov[cur]!=-1) { cov[cur<<1]=cov[cur]; cov[cur<<1|1]=cov[cur]; cov[cur]=-1; /*这个操作很容易忽略,当一个区间得到标记以后,之前的xor[],都没有意义了, 所以要清除*/ xo[cur<<1]=0,xo[cur<<1|1]=0; } if(xo[cur]) { FXOR(cur<<1); FXOR(cur<<1|1); xo[cur]=0; } } void update(char op,int L,int R,int l,int r,int cur) { int m=(l+r)>>1; if(L<=l && r<=R) { if(op=='U')cov[cur]=1,xo[cur]=0; else if(op=='D')cov[cur]=0,xo[cur]=0; else if(op=='C' || op=='S')FXOR(cur); return; } PushDown(cur); if(L<=m)update(op,L,R,l,m,cur<<1); else if(op=='I' || op=='C')cov[cur<<1]=0,xo[cur<<1]=0; if(R>m)update(op,L,R,m+1,r,cur<<1|1); else if(op=='I' || op=='C')cov[cur<<1|1]=0,xo[cur<<1|1]=0; } void query(int l,int r,int cur) { int m=(l+r)>>1; if(cov[cur]!=-1) { if(cov[cur]==0)return; if(now.l==-1) now.l=l,now.r=r; else if(now.r+1>=l)now.r=r; else { if(flag)printf(" "); print_range(now.l,now.r); flag=1; now.l=l,now.r=r; } return; } /*这个PushDown也很容易被忽略,主要是在有xor[]标记的时候要把它更新下去*/ PushDown(cur); query(l,m,cur<<1); query(m+1,r,cur<<1|1); } int main() { char op,l,r; int a,b; cov[1]=0,xo[1]=0; while(scanf("%c %c%d,%d%c\n",&op,&l,&a,&b,&r)!=EOF) { a<<=1;b<<=1; if(l=='(')a++; if(r==')')b--; if( a > b ) { if(op=='I' || op=='C') cov[1]=0,xo[1]=0; } else update(op,a,b,0,(maxn<<1),1); } now.l=-1;now.r=-1; query(0,maxn<<1,1); if(now.l==-1)printf("empty set\n"); else { if (flag)printf(" "); print_range(now.l,now.r); printf("\n"); } return 0; }
hdu 4288(2012 成都网赛A)
题目给出n个操作,包括:向序列中插入x并保证序列单调,删除序列中的x,求序列中位置%5=3的所有元素的和.
网赛时没有想出来,其实就是一个单点更新问题,线段树的每个节点:
sum[rt][i](0<=i<5) 表示该节点管辖的区间[l,r]中所有位置%5为i的元素的和.
cnts[rt] 该节点管辖的区间[l,r]中存在多少个元素.
pushup:
sum[rt][i]=sum[ls][i] + sum[rs][ (i -cnts[ls]%5+5)%5].
为了离散化,需要离线读完全部操作.第一次用stl的unique 和 lower_bound来离散化,感觉效果还可以...
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<map> #define max(a,b) (a)>(b)?(a):(b) #define min(a,b) (a)<(b)?(a):(b) #define maxn 100010 using namespace std; vector<int> V; typedef long long llong; struct IN { char s[4]; int x; };IN op[maxn]; int cnts[maxn<<2],n,cntv; llong sum[maxn<<2][5]; void input() { int i; V.clear(); for(i=0;i<n;i++){ scanf("%s",op[i].s); if(op[i].s[0]=='a' || op[i].s[0]=='d'){ scanf("%d",&op[i].x); V.push_back(op[i].x); } } sort(V.begin(),V.end()); cntv=unique(V.begin(),V.end()) - V.begin(); V.erase(V.begin()+cntv,V.end()); } void build_tree(int l,int r,int rt) { int m=(l+r)>>1; cnts[rt]=0; for(int i=0;i<5;i++)sum[rt][i]=0; if(l==r)return; build_tree(l,m,rt<<1); build_tree(m+1,r,rt<<1|1); } void pushup(int rt) { int ls=rt<<1,rs=rt<<1|1,i,t; cnts[rt]=cnts[ls]+cnts[rs]; for(i=0;i<5;i++){ t=(i-cnts[ls]%5+5)%5; sum[rt][i]=sum[ls][i]+sum[rs][t]; } } void ins(int flg,int pos,int l,int r,int rt) { int m=(l+r)>>1; if(l==r) { cnts[rt]+=flg; sum[rt][1]+=flg*V[pos]; return; } if(pos<=m)ins(flg,pos,l,m,rt<<1); else ins(flg,pos,m+1,r,rt<<1|1); pushup(rt); } int main() { int t,pos; //freopen("test","r",stdin); while (scanf("%d",&n)!=EOF) { input(); build_tree(0,cntv-1,1); for(int i=0;i<n;i++){ if(op[i].s[0]=='a'){ t=op[i].x; pos=lower_bound(V.begin(),V.begin()+cntv,t)-V.begin(); ins(1,pos,0,cntv-1,1); }else if(op[i].s[0]=='d'){ t=op[i].x; pos=lower_bound(V.begin(),V.begin()+cntv,t)-V.begin(); ins(-1,pos,0,cntv-1,1); }else if(op[i].s[0]=='s') printf("%I64d\n",sum[1][3]); } } return 0; }
-----------------------------------更新中-----------------------------------------------------------