ACM大牛总结的线段树专辑_超经典的
- 格式:doc
- 大小:568.50 KB
- 文档页数:18
线段树之hdu4027Can you answer these queries?Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Others)Total Submission(s): 4014 Accepted Submission(s): 979Problem DescriptionA lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of our secret weapon, it could decrease the endurance of a consecutive part of battleships by make their endurance to the square root of it original value of endurance. During the series of attack of our secret weapon, the commander wants to evaluate the effect of the weapon, so he asks you for help.Y ou are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.Notice that the square root operation should be rounded down to integer.InputThe input contains several test cases, terminated by EOF.For each test case, the first line contains a single integer N, denoting there are N battleships of evil in a line. (1 <= N <= 100000)The second line contains N integers Ei, indicating the endurance value of each battleship from the beginning of the line to the end. Y ou can assume that the sum of all endurance value is less than 263.The next line contains an integer M, denoting the number of actions and queries. (1 <= M <= 100000)For the following M lines, each line contains three integers T, X and Y. The T=0 denoting the action of the secret weapon, which will decrease the endurance value of the battleships between the X-th and Y-th battleship, inclusive. The T=1 denoting the query of the commander which ask for the sum of the endurance value of the battleship between X-th and Y-th, inclusive.OutputFor each test case, print the case number at the first line. Then print one line for each query. And remember follow a blank line after each test case.Sample Input101 2 3 4 5 6 7 8 9 1050 1 101 1 101 1 50 5 81 4 8Sample OutputCase #1:1976Source/*一个10^5的序列,有10^5个操作,每个操作为a,b,ca=0时将b到c区间内的数都开根号下取整,a=1时求b到c段的和其中所有数的和不超过2^63。
免费模板~~~ACM Standard Code LibraryHuang WeiSoftware EngineeringComputer and Software CollegeHangzhou Dianzi UniversityOctober,2008ACM算法模板集Contents一.常用函数与STL二.重要公式与定理1.Fibonacci Number2.Lucas Number3.Catalan Number4.Stirling Number(Second Kind)5.Bell Number6.Stirling's Approximation7.Sum of Reciprocal Approximation8.Young Tableau9.整数划分10.错排公式11.三角形内切圆半径公式12.三角形外接圆半径公式13.圆內接四边形面积公式14.基础数论公式三.大数模板,字符读入四.数论算法1.Greatest Common Divisor最大公约数2.Prime素数判断3.Sieve Prime素数筛法4.Module Inverse模逆元5.Extended Euclid扩展欧几里德算法6.Modular Linear Equation模线性方程(同余方程)7.Chinese Remainder Theorem中国余数定理(互素与非互素)8.Euler Function欧拉函数9.Farey总数9.Farey序列构造ler_Rabbin素数测试,Pollard_rho因式分解五.图论算法1.最小生成树(Kruscal算法)2.最小生成树(Prim算法)3.单源最短路径(Bellman-ford算法)4.单源最短路径(Dijkstra算法)5.全源最短路径(Folyd算法)6.拓扑排序7.网络预流和最大流8.网络最小费用最大流9.网络最大流(高度标号预流推进)10.最大团11.最大匹配(Hungary,HopcroftKarp算法)12.带权二分图最优匹配(KM算法)13.强连通分量(Kosaraju算法)14.强连通分量(Gabow算法)15.无向图割边割点和双连通分量16.最小树形图O(N^3)17.最小树形图O(VE)六.几何算法1.几何模板2.球面上两点最短距离3.三点求圆心坐标4.三角形几个重要的点七.专题讨论1.树状数组2.字典树3.后缀树4.线段树5.并查集6.二叉堆7.逆序数(归并排序)8.树状DP9.欧拉路10.八数码11.高斯消元法12.字符串匹配(KMP算法)13.全排列,全组合14.二维线段树15.稳定婚姻匹配16.后缀数组17.左偏树18.标准RMQ-ST19.度限制最小生成树20.最优比率生成树(0/1分数规划)21.最小花费置换22.区间K大数23.LCA-RMQ-ST24.LCA–Tarjan25.指数型母函数26.指数型母函数(大数据)27.AC自动机(字典树+KMP)28.FFT(大数乘法)29.二分图网络最大流最小割30.混合图欧拉回路31.无源汇上下界网络流32.二分图最小点权覆盖33.带约束的轨道计数(Burnside引理)34.三分法求函数波峰35.单词计数,DFA自动机,Trie图(完全AC自动机)36.字符串和数值hash37.滚动队列,前向星表示法38.最小点基,最小权点基39.LCSubsequence O(N^2/logN)40.伸展树41.Treap42.0/1分数规划K约束43.表达式求值44.乘除法博弈,Wythoff博弈45.状态压缩的积木型DP46.解一般线性方程组(消元法)47.块状链表48.Factor Oracle第一章常用函数和STL一.常用函数#include<stdio.h>int getchar(void);//读取一个字符,一般用来去掉无用字符char*gets(char*str);//读取一行字符串#include<stdlib.h>void*malloc(size_t size);//动态内存分配,开辟大小为size的空间void qsort(void*buf,size_t num,size_t size,int(*compare)(const void*,const void*));//快速排序Sample:int compare_ints(const void*a,const void*b){int*arg1=(int*)a;int*arg2=(int*)b;if(*arg1<*arg2)return-1;else if(*arg1==*arg2)return0;else return1;}int array[]={-2,99,0,-743,2,3,4};int array_size=7;qsort(array,array_size,sizeof(int),compare_ints);#include<math.h>//求反正弦,arg∈[-1,1],返回值∈[-pi/2,+pi/2]double asin(double arg);//求正弦,arg为弧度,弧度=角度*Pi/180.0,返回值∈[-1,1]double sin(double arg);//求e的arg次方double exp(double arg);//求num的对数,基数为edouble log(double num);//求num的根double sqrt(double num);//求base的exp次方double pow(double base,double exp);#include<string.h>//初始化内存,常用来初始化数组void*memset(void*buffer,int ch,size_t count);memset(the_array,0,sizeof(the_array));//printf是它的变形,常用来将数据格式化为字符串int sprintf(char*buffer,const char*format,...);sprintf(s,"%d%d",123,4567);//s="1234567"//scanf是它的变形,常用来从字符串中提取数据int sscanf(const char*buffer,const char*format,...);Sample:char result[100]="24hello",str[100];int num;sprintf(result,"%d%s",num,str);//num=24;str="hello";//字符串比较,返回值<0代表str1<str2,=0代表str1=str2,>0代表str1>str2 int strcmp(const char*str1,const char*str2);二.常用STL[标准container概要]vector<T>大小可变的向量,类似数组的用法,容易实现删除list<T>双向链表queue<T>队列,empty(),front(),pop(),push()stack<T>栈,empty(),top(),pop(),push()priority_queue<T>优先队列,empty(),top(),pop(),push()set<T>集合map<key,val>关联数组,常用来作hash映射[标准algorithm摘录]for_each()对每一个元素都唤起(调用)一个函数find()查找第一个能与引数匹配的元素replace()用新的值替换元素,O(N)copy()复制(拷贝)元素,O(N)remove()移除元素reverse()倒置元素sort()排序,O(N log(N))partial_sort()部分排序binary_search()二分查找merge()合并有序的序列,O(N)[C++String摘录]copy()从别的字符串拷贝empty()判断字符串是否为空erase()从字符串移除元素find()查找元素insert()插入元素length()字符串长度replace()替换元素substr()取子字符串swap()交换字符串第二章重要公式与定理1.Fibonacci Number0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610…Formula:011211i i i n n F F F F F F −−===+⎡⎤⎥==⎥⎠⎦2.Lucas Number1,3,4,7,11,18,29,47,76,123...Formula:n nn L =+⎝⎠⎝⎠3.Catalan Number1,2,5,14,42,132,429,1430,4862,16796,58786,208012…Formula:110(2,)1*n n n i n ii C n n Cat n Cat Cat Cat −−−==+=∑Application:1)将n +2边形沿弦切割成n 个三角形的不同切割数Sample:n =2;n =3;2)n +1个数相乘,给每两个元素加上括号的不同方法数Sample:n =2;(1(23)),((12)3)n =3;(1(2(34))),(1((23)4)),((12)(34)),((1(23))4),(((12)3)4)3)n 个节点的不同形状的二叉树数(严《数据结构》P .155)4)从n *n 方格的左上角移动到右下角不升路径数Sample:n =2;n =3;4.Stirling Number(Second Kind)S(n,m)表示含n 个元素的集合划分为m 个集合的情况数或者是n 个有标号的球放到m 个无标号的盒子中,要求无一为空,其不同的方案数Formula:,1,11,0 (0 || ) (1)n m n m n m m n m S S m S n m −−−=<⎧⎨+×>≥⎩,01(1)(,)()!m i n n m i S C m i m i m ==−××−∑Special Cases:,0,11,2,3,1,01211(3323)6(,2)1n n n n n n n n n n n S S S S S C n S −−===−=−×+==5.Bell Numbern 个元素集合所有的划分数Formula:,0nn n ii B S ==∑6.Stirling's Approximation!nn n e ⎞=⎟⎠7.Sum of Reciprocal ApproximationEulerGamma =0.57721566490153286060651209;11ln() ()n i n EulerGamma n i==+→∞∑8.Young TableauYoung Tableau(杨式图表)是一个矩阵,它满足条件:如果格子[i,j]没有元素,则[i+1,j]也一定没有元素如果格子[i,j]有元素a[i,j],则[i+1,j]要么没有元素,要么a[i+1,j]>a[i,j]Y[n]代表n 个数所组成的杨式图表的个数Formula:121212(1) (2)n n n Y Y Y Y n Y n −−===+−×>Sample:n =3;9.整数划分将整数n 分成k 份,且每份不能为空,任意两种分法不能相同1)不考虑顺序for (int p=1;p<=n ;p++)for (int i=p;i<=n ;i++)for (int j=k;j>=1;j--)dp[i][j]+=dp[i-p][j-1];cout<<dp[n][k]<<endl;2)考虑顺序dp[i][j]=dp[i-k][j-1];(k=1..i)3)若分解出来的每个数均有一个上限mdp[i][j]=dp[i-k][j-1];(k=1..m)10.错排公式121201(1)()n n n D D D n D D −−===−×+11.三角形内切圆半径公式22a b cp s sr a b c++===++12.三角形外接圆半径公式4abcR s=13.圆內接四边形面积公式2a b c dp s +++==14.基础数论公式1)模取幂%((((%)*)%))%n a b a b a b b=…2)n 的约数的个数若n 满足1212m n n n m n p p p =+++…,则n 的约数的个数为12(1)(1)(1)m n n n +++…第三章大数模板typedef int hugeint;//应不大于,以防乘法时溢出const int Base=1000;const int Capacity=1000;struct xnum{int Len;int Data[Capacity];xnum():Len(0){}xnum(const xnum&V):Len(V.Len){memcpy(Data,V.Data,Len*sizeof*Data);}xnum(int V):Len(0){for(;V>0;V/=Base)Data[Len++]=V%Base;}xnum(char S[]);xnum&operator=(const xnum&V){Len=V.Len;memcpy(Data,V.Data,Len*sizeof*Data);return*this;}int&operator[](int Index){return Data[Index];}int operator[](int Index)const{return Data[Index];}void print(){printf("%d",Len==0?0:Data[Len-1]);for(int i=Len-2;i>=0;i--)for(int j=Base/10;j>0;j/=10)printf("%d",Data[i]/j%10);}};xnum::xnum(char S[]){int I,J;Data[Len=0]=0;J=1;for(I=strlen(S)-1;I>=0;I--){Data[Len]+=(S[I]-'0')*J;J*=10;if(J>=Base)J=1,Data[++Len]=0;}if(Data[Len]>0)Len++;}int compare(const xnum&A,const xnum&B){int I;if(A.Len!=B.Len)return A.Len>B.Len?1:-1;for(I=A.Len-1;I>=0&&A[I]==B[I];I--);if(I<0)return0;return A[I]>B[I]?1:-1;}xnum operator+(const xnum&A,const xnum&B){xnum R;int I;int Carry=0;for(I=0;I<A.Len||I<B.Len||Carry>0;I++) {if(I<A.Len)Carry+=A[I];if(I<B.Len)Carry+=B[I];R[I]=Carry%Base;Carry/=Base;}R.Len=I;return R;}xnum operator-(const xnum&A,const xnum&B){xnum R;int Carry=0;R.Len=A.Len;int I;for(I=0;I<R.Len;I++){R[I]=A[I]-Carry;if(I<B.Len)R[I]-=B[I];if(R[I]<0)Carry=1,R[I]+=Base;else Carry=0;}while(R.Len>0&&R[R.Len-1]==0)R.Len--;return R;}xnum operator*(const xnum&A,const int B){int I;if(B==0)return0;xnum R;hugeint Carry=0;for(I=0;I<A.Len||Carry>0;I++){if(I<A.Len)Carry+=hugeint(A[I])*B;R[I]=Carry%Base;Carry/=Base;}R.Len=I;return R;}xnum operator*(const xnum&A,const xnum&B){int I;if(B.Len==0)return0;xnum R;for(I=0;I<A.Len;I++){hugeint Carry=0;for(int J=0;J<B.Len||Carry>0;J++){if(J<B.Len)Carry+=hugeint(A[I])*B[J];if(I+J<R.Len)Carry+=R[I+J];if(I+J>=R.Len)R[R.Len++]=Carry%Base;else R[I+J]=Carry%Base;Carry/=Base;}}return R;}xnum operator/(const xnum&A,const int B){xnum R;int I;hugeint C=0;for(I=A.Len-1;I>=0;I--){C=C*Base+A[I];R[I]=C/B;C%=B;}R.Len=A.Len;while(R.Len>0&&R[R.Len-1]==0)R.Len--;return R;}//divxnum operator/(const xnum&A,const xnum&B){int I;xnum R,Carry=0;int Left,Right,Mid;for(I=A.Len-1;I>=0;I--){Carry=Carry*Base+A[I];Left=0;Right=Base-1;while(Left<Right){Mid=(Left+Right+1)/2;if(compare(B*Mid,Carry)<=0)Left=Mid;else Right=Mid-1;}R[I]=Left;Carry=Carry-B*Left;}R.Len=A.Len;while(R.Len>0&&R[R.Len-1]==0)R.Len--;return R;}//modxnum operator%(const xnum&A,const xnum&B){int I;xnum R,Carry=0;int Left,Right,Mid;for(I=A.Len-1;I>=0;I--){Carry=Carry*Base+A[I];Left=0;Right=Base-1;while(Left<Right){Mid=(Left+Right+1)/2;if(compare(B*Mid,Carry)<=0)Left=Mid;else Right=Mid-1;}R[I]=Left;Carry=Carry-B*Left;}R.Len=A.Len;while(R.Len>0&&R[R.Len-1]==0)R.Len--;return Carry;}istream&operator>>(istream&In,xnum&V){char Ch;for(V=0;In>>Ch;){V=V*10+(Ch-'0');if(cin.peek()<='')break;}return In;}ostream&operator<<(ostream&Out,const xnum&V){int I;Out<<(V.Len==0?0:V[V.Len-1]);for(I=V.Len-2;I>=0;I--)for(int J=Base/10;J>0;J/=10)Out<<V[I]/J%10;return Out;}xnum gcd(xnum a,xnum b){if(compare(b,0)==0)return a;else return gcd(b,a%b);}int div(char*A,int B){int I;int C=0;int Alen=strlen(A);for(I=0;I<Alen;I++){C=C*Base+A[I]-'0';C%=B;}return C;}xnum C(int n,int m){int i;xnum sum=1;for(i=n;i>=n-m+1;i--)sum=sum*i;for(i=1;i<=m;i++)sum=sum/i;return sum;}#define MAXN9999#define DLEN4class BigNum{private:int a[1000];//可以控制大数的位数int len;//大数长度public:BigNum(){len=1;memset(a,0,sizeof(a));} BigNum(const int);BigNum(const char*);BigNum(const BigNum&);BigNum&operator=(const BigNum&);BigNum operator+(const BigNum&)const;BigNum operator-(const BigNum&)const;BigNum operator*(const BigNum&)const;BigNum operator/(const int&)const;BigNum operator^(const int&)const;int operator%(const int&)const;bool operator>(const BigNum&T)const;void print();};BigNum::BigNum(const int b){int c,d=b;len=0;memset(a,0,sizeof(a));while(d>MAXN){c=d-(d/(MAXN+1))*(MAXN+1);d=d/(MAXN+1);a[len++]=c;}a[len++]=d;}BigNum::BigNum(const char*s){int t,k,index,l,i;memset(a,0,sizeof(a));l=strlen(s);len=l/DLEN;if(l%DLEN)len++;index=0;for(i=l-1;i>=0;i-=DLEN){t=0;k=i-DLEN+1;if(k<0)k=0;for(int j=k;j<=i;j++)t=t*10+s[j]-'0';a[index++]=t;}}BigNum::BigNum(const BigNum&T):len(T.len){ int i;memset(a,0,sizeof(a));for(i=0;i<len;i++)a[i]=T.a[i];}BigNum&BigNum::operator=(const BigNum&n){ len=n.len;memset(a,0,sizeof(a));int i;for(i=0;i<len;i++)a[i]=n.a[i];return*this;}BigNum BigNum::operator+(const BigNum&T)const{ BigNum t(*this);int i,big;//位数big=T.len>len?T.len:len;for(i=0;i<big;i++){t.a[i]+=T.a[i];if(t.a[i]>MAXN){t.a[i+1]++;t.a[i]-=MAXN+1;}}if(t.a[big]!=0)t.len=big+1;else t.len=big;return t;}BigNum BigNum::operator-(const BigNum&T)const{ int i,j,big;bool flag;BigNum t1,t2;if(*this>T){t1=*this;t2=T;flag=0;}else{t1=T;t2=*this;flag=1;}big=t1.len;for(i=0;i<big;i++){if(t1.a[i]<t2.a[i]){j=i+1;while(t1.a[j]==0)j++;t1.a[j--]--;while(j>i)t1.a[j--]+=MAXN;t1.a[i]+=MAXN+1-t2.a[i];}else t1.a[i]-=t2.a[i];}t1.len=big;while(t1.a[len-1]==0&&t1.len>1){t1.len--;big--;}if(flag)t1.a[big-1]=0-t1.a[big-1];return t1;}BigNum BigNum::operator*(const BigNum&T)const{BigNum ret;int i,j,up;int temp,temp1;for(i=0;i<len;i++){up=0;for(j=0;j<T.len;j++){temp=a[i]*T.a[j]+ret.a[i+j]+up;if(temp>MAXN){temp1=temp-temp/(MAXN+1)*(MAXN+1);up=temp/(MAXN+1);ret.a[i+j]=temp1;}else{up=0;ret.a[i+j]=temp;}}if(up!=0)ret.a[i+j]=up;}ret.len=i+j;while(ret.a[ret.len-1]==0&&ret.len>1)ret.len--;return ret;}BigNum BigNum::operator/(const int&b)const{BigNum ret;int i,down=0;for(i=len-1;i>=0;i--){ret.a[i]=(a[i]+down*(MAXN+1))/b;down=a[i]+down*(MAXN+1)-ret.a[i]*b;}ret.len=len;while(ret.a[ret.len-1]==0&&ret.len>1)ret.len--;return ret;}int BigNum::operator%(const int&b)const{int i,d=0;for(i=len-1;i>=0;i--){d=((d*(MAXN+1))%b+a[i])%b;}return d;}BigNum BigNum::operator^(const int&n)const{BigNum t,ret(1);if(n<0)exit(-1);if(n==0)return1;if(n==1)return*this;int m=n;while(m>1){t=*this;int i;for(i=1;i<<1<=m;i<<=1){t=t*t;}m-=i;ret=ret*t;if(m==1)ret=ret*(*this);}return ret;}bool BigNum::operator>(const BigNum&T)const{ int ln;if(len>T.len)return true;else if(len==T.len){ln=len-1;while(a[ln]==T.a[ln]&&ln>=0)ln--;if(ln>=0&&a[ln]>T.a[ln])return true;else return false;}else return false;}void BigNum::print(){int i;cout<<a[len-1];for(i=len-2;i>=0;i--){cout.width(DLEN);cout.fill('0');cout<<a[i];}}//读取整数const int ok=1;int get_val(int&ret){ret=0;char ch;while((ch=getchar())>'9'||ch<'0');do{ret=ret*10+ch-'0';}while((ch=getchar())<='9'&&ch>='0');return ok;}//带负数int get_val(int&ret){ret=0;char ch;bool neg=false;while(((ch=getchar())>'9'||ch<'0')&&ch!='-');if(ch=='-'){neg=true;while((ch=getchar())>'9'||ch<'0');}do{ret=ret*10+ch-'0';}while((ch=getchar())<='9'&&ch>='0');ret=(neg?-ret:ret);return ok;}//读取整数,可判EOF和EOLconst int eof=-1;const int eol=-2;int get_val(int&ret){ret=0;char ch;while(((ch=getchar())>'9'||ch<'0')&&ch!=EOF);if(ch==EOF)return eof;do{ret=ret*10+ch-'0';}while((ch=getchar())<='9'&&ch>='0');if(ch=='\n')return eol;return ok;}//读取浮点数int get_val(double&ret){ret=0;double base=0.1;char ch;bool dot=false,neg=false;while(((ch=getchar())>'9'||ch<'0')&&ch!='.'&&ch!='-');if(ch=='-'){neg=true;while(((ch=getchar())>'9'||ch<'0')&&ch!='.'&&ch!='-');}do{if(ch=='.'){dot=true;continue;}if(dot){ret+=(ch-'0')*base;base*=0.1;}else ret=ret*10+(ch-'0');}while(((ch=getchar())<='9'&&ch>='0')||ch=='.');ret=(neg?-ret:ret);return ok;}typedef long long LL;//LL MultiMod(LL a,LL b,LL c){//if(b)//return(a*(b&1)%c+(MultiMod(a,b>>1,c)<<1))%c; //return0;//}LL MultiMod(LL a,LL b,LL c){LL ret=0,d=a;for(;b;b>>=1,d<<=1,d%=c)if((b&1))ret=(ret+d)%c;return ret;}//128-bits integer's power with mod in O(64*LogN)LL ModPower(LL base,LL exp,LL mod){LL ret=1;for(;exp;exp>>=1,base=MultiMod(base,base,mod)) if((exp&1))ret=MultiMod(ret,base,mod);return ret;}第四章数论算法1.Greatest Common Divisor最大公约数int GCD(int x,int y){int t;while(y>0){t=x%y;x=y;y=t;}return x;}2.Prime素数判断bool is_prime(int u){if(u==0||u==1)return false;if(u==2)return true;if(u%2==0)return false;for(int i=3;i<=sqrt(u);i+=2)if(u%i==0)return false;return true;}3.Sieve Prime素数筛法const int M=1000;//M:sizebool mark[M];//true:prime numbervoid sieve_prime(){memset(mark,true,sizeof(mark));mark[0]=mark[1]=false;for(int i=2;i<=sqrt(M);i++){if(mark[i]){for(int j=i*i;j<M;j+=i)mark[j]=false;}}}4.Module Inverse模逆元//ax≡1(mod n)int Inv(int a,int n){int d,x,y;d=extended_euclid(a,n,x,y);if(d==1)return(x%n+n)%n;else return-1;//no solution}5.Extended Euclid扩展欧几里德算法//如果GCD(a,b)=d,则存在x,y,使d=ax+by//extended_euclid(a,b)=ax+byint extended_euclid(int a,int b,int&x,int&y){int d;if(b==0){x=1;y=0;return a;}d=extended_euclid(b,a%b,y,x);y-=a/b*x;return d;}6.Modular Linear Equation模线性方程(同余方程) //如果GCD(a,b)不能整除c,则ax+by=c没有整数解//ax≡b(mod n)n>0//上式等价于二元一次方程ax–ny=bvoid modular_linear_equation(int a,int b,int n){int d,x,y,x0,gcd;//可以减少扩展欧几里德溢出的可能gcd=GCD(a,n);if(b%gcd!=0){cout<<"no solution"<<endl;return;}a/=gcd;b/=gcd;n/=gcd;d=extended_euclid(a,n,x,y);if(b%d==0){x0=(x*(b/d))%n;//x0:basic solutionint ans=n;//min x=(x0%(n/d)+(n/d))%(n/d)for(int i=0;i<d;i++){ans=(x0+i*(n/d))%n;cout<<ans<<endl;}}else cout<<"no solution"<<endl;}7.Chinese Remainder Theorem中国余数定理//x≡b[i](mod w[i]),i∈[1,len-1]//前提条件w[i]>0,且w[]中任意两个数互质int chinese_remainder(int b[],int w[],int len){int i,d,x,y,m,n,ret;ret=0;n=1;for(i=0;i<len;i++)n*=w[i];for(i=0;i<len;i++){m=n/w[i];d=extended_euclid(w[i],m,x,y);ret=(ret+y*m*b[i])%n;}return(n+ret%n)%n;}//m≡r[i](mod a[i])//a[i]可以不互素//-1表示无解/*Pku2891Strange Way to Express Integers假设C≡A1(mod B1),C≡A2(mod B2)。
线段树维护标记经典题
线段树是一种用于解决区间查询问题的数据结构,它可以高效地处理区间操作,例如区间最大值、区间和、区间更新等。
线段树通常用于解决动态区间查询问题,比如区间修改、区间查询等。
在线段树中,维护标记是一种常见的技巧,它可以帮助我们在区间内进行延迟更新,从而减少不必要的操作。
维护标记通常用于延迟更新操作,当我们需要对区间内的元素进行更新时,我们可以先将更新操作记录在节点上,而不立即更新区间内的所有元素,这样可以减少不必要的更新操作,提高效率。
经典题目中常涉及使用线段树维护标记的问题包括区间修改、区间查询等。
例如,区间修改问题可以是给定一个数组,进行一系列区间修改操作,然后查询某个位置的元素值;区间查询问题可以是给定一个数组,进行一系列区间查询操作,然后求解区间内的最大值、最小值、和等。
在解决经典题目时,我们需要注意线段树的构建、更新、查询等操作,以及维护标记的技巧,这样才能高效地解决问题。
同时,我们也需要注意处理边界情况和特殊情况,保证算法的正确性和鲁
棒性。
总之,线段树是一种非常有用的数据结构,通过合理地维护标记,我们可以解决许多经典的区间查询问题。
在解决这些问题时,
我们需要充分理解线段树的原理和操作,灵活运用维护标记的技巧,才能更好地解决问题。
可持久化线段树总结(可持久化线段树,线段树)最近正在学习⼀种数据结构——可持久化线段树。
看了⽹上的许多博客,弄了⼏道模板题,思路有点乱了,所以还是来总结整理下吧。
可持久化线段树⾸先要了解此数据结构的基础——线段树。
百度⼀下,你就知道!推荐⼀下,对线段树的基本操作讲得挺详细的。
为了更好地理清思路,我在这⾥先放个模板题吧。
题⽬描述你需要维护这样的⼀个长度为\(N\)的数组,⽀持如下⼏种操作1. 在某个历史版本上修改某⼀个位置上的值2. 访问某个历史版本上的某⼀位置的值此外,每进⾏⼀次操作(对于操作2,即为⽣成⼀个完全⼀样的版本,不作任何改动),就会⽣成⼀个新的版本。
版本编号即为当前操作的编号(从1开始编号,版本0表⽰初始状态数组)输⼊输出格式输⼊格式:输⼊的第⼀⾏包含两个正整数\(N,M\)分别表⽰数组的长度和操作的个数。
第⼆⾏包含 N N个整数,依次为初始状态下数组各位的值(依次为\(a_i, 1 \leq i \leq N\))。
接下来\(M\)⾏每⾏包含3或4个整数,代表两种操作之⼀(\(i\)为基于的历史版本号):对于操作1,格式为\(v_i \ 1 \ {loc}_i \ {value}_i v\),即为在版本\(v_i\)的基础上,将\(a_{{loc}_i}\)修改为\({value}_i\)对于操作2,格式为\(v_i \ 2 \ {loc}_i\),即访问版本\(v_i\)中的\(a_{{loc}_i}\)的值输出格式:输出包含若⼲⾏,依次为每个操作2的结果。
输⼊输出样例输⼊样例#1:5 1059 46 14 87 410 2 10 1 1 140 1 1 570 1 1 884 2 40 2 50 2 44 2 12 2 21 1 5 91输出样例#1:598741878846说明数据规模:对于30%的数据:\(1 \leq N, M \leq {10}^3\)对于50%的数据:\(1 \leq N, M \leq {10}^4\)对于70%的数据:\(1 \leq N, M \leq {10}^5\)对于100%的数据:\(1 \leq N, M \leq {10}^6, 1 \leq {loc}_i \leq N, 0 \leq v_i < i, -{10}^9 \leq a_i, {value}_i \leq {10}^9\)经测试,正常常数的可持久化数组可以通过,请各位放⼼数据略微凶残,请注意常数不要过⼤另,此题I/O量较⼤,如果实在TLE请注意I/O优化询问⽣成的版本是指你访问的那个版本的复制样例说明:⼀共11个版本,编号从0-10,依次为:0 : 59 46 14 87 411 : 59 46 14 87 412 : 14 46 14 87 413 : 57 46 14 87 414 : 88 46 14 87 415 : 88 46 14 87 416 : 59 46 14 87 417 : 59 46 14 87 418 : 88 46 14 87 419 : 14 46 14 87 4110 : 59 46 14 87 91思路分析很裸的可持久化线段树板⼦题。
∙ 【完全版】线段树很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pku 打广告,但是现在我自己都不太好意思去看那篇文章了,觉得当时的代码风格实在是太丑了,很多线段树的初学者可能就是看着这篇文章来练习的,如果不小心被我培养出了这么糟糕的风格,实在是过意不去,正好过几天又要给集训队讲解线段树,所以决定把这些题目重新写一遍,顺便把近年我接触到的一些新题更新上去~;并且学习了splay 等更高级的数据结构后对线段树的体会有更深了一层,线段树的写法也就比以前飘逸,简洁且方便多了.在代码前先介绍一些我的线段树风格: ∙maxn 是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn 的最小2x的两倍 ∙lson 和rson 分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示 ∙ 以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson 和rson 的预定义可以很方便∙PushUP(int rt)是把当前结点的信息更新到父结点 ∙PushDown(int rt)是把当前结点的信息更新给儿子结点 ∙ rt 表示当前子树的根(root),也就是当前所在的结点 整理这些题目后我觉得线段树的题目整体上可以分成以下四个部分: ∙ 单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来o hdu1166 敌兵布阵题意:O(-1)思路:O(-1)线段树功能:update:单点增减 query:区间求和?View Code CPP1 2 3 4 5 6 78 9 10 11 1213 14 15 16 17 1819 20 21#include <cstdio> #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 55555; int sum [maxn <<2]; void PushUP (int rt ) { sum [rt ] = sum [rt <<1] + sum [rt <<1|1]; } void build (int l,int r,int rt ) { if (l == r ) {scanf ("%d",&sum [rt ]); return ; } int m = (l + r ) >> 1; build (lson );build (rson );24 25 2627 28 29 30 31 3233 34 35 36 37 3839 40 41 42 434445 46 47 48 495051 52 53 54 5556 57 58 } void update (int p,int add,int l,int r,int rt ) { if (l == r ) { sum [rt ] += add ;return ; } int m = (l + r ) >> 1; if (p <= m ) update (p , add , lson ); else update (p , add , rson );PushUP (rt ); } int query (int L,int R,int l,int r,int rt ) { if (L <= l && r <= R ) { return sum [rt ]; } int m = (l + r ) >> 1; int ret = 0; if (L <= m ) ret += query (L , R , lson ); if (R > m ) ret += query (L , R , rson ); return ret ; } int main () { int T , n ; scanf ("%d",&T ); for (int cas = 1 ; cas <= T ; cas ++) { printf ("Case %d:\n ",cas ); scanf ("%d",&n ); build (1 , n , 1);char op [10]; while (scanf ("%s",op )) {if (op [0] == 'E') break ;int a , b ;scanf ("%d%d",&a,&b );if (op [0] == 'Q') printf ("%d \n ",query (a , b , 1 , n , 1));else if (op [0] == 'S') update (a , -b , 1 , n , 1); else update (a , b , 1 , n , 1);}}return 0;}o hdu1754 I Hate It题意:O(-1)思路:O(-1)线段树功能:update:单点替换 query:区间最值?View Code CPP1 2 3 4 5 6 78 9 10#include <cstdio> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 222222; int MAX [maxn <<2];13141516171819202122232425262728293031323334353637383940414243444546474849505152535455 MAX[rt]= max(MAX[rt<<1] , MAX[rt<<1|1]);}void build(int l,int r,int rt){if(l == r){scanf("%d",&MAX[rt]);return;}int m =(l + r)>>1;build(lson);build(rson);PushUP(rt);}void update(int p,int sc,int l,int r,int rt){if(l == r){MAX[rt]= sc;return;}int m =(l + r)>>1;if(p <= m) update(p , sc , lson);else update(p , sc , rson);PushUP(rt);}int query(int L,int R,int l,int r,int rt){if(L <= l && r <= R){return MAX[rt];}int m =(l + r)>>1;int ret =0;if(L <= m) ret = max(ret , query(L , R , lson));if(R > m) ret = max(ret , query(L , R , rson));return ret;}int main(){int n , m;while(~scanf("%d%d",&n,&m)){build(1 , n , 1);while(m --){char op[2];int a , b;scanf("%s%d%d",op,&a,&b);if(op[0]=='Q')printf("%d\n",query(a , b , 1 , n , 1));else update(a , b , 1 , n , 1);}}return0;}o hdu1394 Minimum Inversion Number题意:求Inversion后的最小逆序数思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解线段树功能:update:单点增减 query:区间求和?View Code CPP1 #include <cstdio>2 3 4 5 6 7 89 10 11 12 1314 15 16 17 18 1920 21 22 23 242526 27 28 29 303132 33 34 35 363738 39 40 41 4243 44 45 46 47 4849 50 51 52 535455 56 57 58 #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 5555; int sum [maxn <<2]; void PushUP (int rt ) { sum [rt ] = sum [rt <<1] + sum [rt <<1|1]; } void build (int l,int r,int rt ) {sum [rt ] = 0; if (l == r ) return ; int m = (l + r ) >> 1; build (lson ); build (rson ); } void update (int p,int l,int r,int rt ) { if (l == r ) { sum [rt ] ++; return ; } int m = (l + r ) >> 1; if (p <= m ) update (p , lson ); else update (p , rson ); PushUP (rt ); } int query (int L,int R,int l,int r,int rt ) { if (L <= l && r <= R ) { return sum [rt ]; } int m = (l + r ) >> 1; int ret = 0; if (L <= m ) ret += query (L , R , lson ); if (R > m ) ret += query (L , R , rson );return ret ; } int x [maxn ]; int main () { int n ;while (~scanf ("%d",&n )) { build (0 , n - 1 , 1); int sum = 0; for (int i = 0 ; i < n ; i ++) { scanf ("%d",&x [i ]); sum += query (x [i ] , n - 1 , 0 , n - 1 , 1); update (x [i ] , 0 , n - 1 , 1); }int ret = sum ;for (int i = 0 ; i < n ; i ++) {sum += n - x [i ] - x [i ] - 1;ret = min (ret , sum );}printf ("%d \n ",ret );}return 0;}o hdu2795 Billboard题意:h*w 的木板,放进一些1*L 的物品,求每次放空间能容纳且最上边的位子思路:每次找到最大值的位子,然后减去L线段树功能:query:区间求最大值的位子(直接把update 的操作在query 里做了)?View Code CPP1 2 3 4 5 6 78 9 10 11 1213 14 15 16 17 1819 20 21 22 23 2425 26 27 28 293031 32 33 34 35 3637 38 39 40 4142 #include <cstdio> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 222222; int h , w , n ; int MAX [maxn <<2]; void PushUP (int rt ) { MAX [rt ] = max (MAX [rt <<1] , MAX [rt <<1|1]);} void build (int l,int r,int rt ) { MAX [rt ] = w ; if (l == r ) return ; int m = (l + r ) >> 1;build (lson ); build (rson ); } int query (int x,int l,int r,int rt ) { if (l == r ) {MAX [rt ] -= x ; return l ; } int m = (l + r ) >> 1; int ret = (MAX [rt <<1] >= x ) ? query (x , lson ) : query (x , rson ); PushUP (rt ); return ret ; } int main () { while (~scanf ("%d%d%d",&h,&w,&n )) { if (h > n ) h = n ; build (1 , h , 1); while (n --) { int x ;scanf ("%d",&x );if (MAX [1] < x ) puts ("-1");else printf ("%d \n ",query (x , 1 , h , 1));}}return 0;}练习:成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or 询问到的时候o hdu1698 Just a Hook题意:O(-1)思路:O(-1)线段树功能:update:成段替换 (由于只query 一次总区间,所以可以直接输出1结点的信息)?View Code CPP1 2 3 4 5 6 78 9 10 11 1213 14 15 16 17 1819 20 21 22 232425 26 27 28 293031 32 33 34 35 3637 38 39 40 4142 43 44 45 46 4748 49 50 51 52 5354 #include <cstdio> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 111111; int h , w , n ; int col [maxn <<2]; int sum [maxn <<2]; void PushUp (int rt ) {sum [rt ] = sum [rt <<1] + sum [rt <<1|1]; } void PushDown (int rt,int m ) { if (col [rt ]) { col [rt <<1] = col [rt <<1|1] = col [rt ];sum [rt <<1] = (m - (m >> 1)) * col [rt ]; sum [rt <<1|1] = (m >> 1) * col [rt ]; col [rt ] = 0; } }void build (int l,int r,int rt ) { col [rt ] = 0; sum [rt ] = 1; if (l == r ) return ; int m = (l + r ) >> 1; build (lson ); build (rson ); PushUp (rt ); } void update (int L,int R,int c,int l,int r,int rt ) { if (L <= l && r <= R ) { col [rt ] = c ; sum [rt ] = c * (r - l + 1); return ;} PushDown (rt , r - l + 1); int m = (l + r ) >> 1; if (L <= m ) update (L , R , c , lson ); if (R > m ) update (L , R , c , rson );PushUp (rt ); } int main () { int T , n , m ; scanf ("%d",&T );55 56 57 for (int cas = 1 ; cas <= T ; cas ++) { scanf ("%d%d",&n,&m );build (1 , n , 1);while (m --) {int a , b , c ;scanf ("%d%d%d",&a,&b,&c );update (a , b , c , 1 , n , 1);}printf ("Case %d: The total value of the hook is %d.\n ",cas , sum [1]);}return 0;} o poj3468 A Simple Problem with Integers题意:O(-1)思路:O(-1)线段树功能:update:成段增减 query:区间求和?View Code CPP1 2 3 4 5 6 78 9 10 11 12 1314 15 16 17 1819 20 21 22 23 2425 26 27 28 29 3031 32 33 34 35 3637 38 39 40 414243 44 45 46#include <cstdio> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 #define LL long long const int maxn = 111111; LL add [maxn <<2]; LL sum [maxn <<2]; void PushUp (int rt ) { sum [rt ] = sum [rt <<1] + sum [rt <<1|1]; } void PushDown (int rt,int m ) { if (add [rt ]) { add [rt <<1] += add [rt ];add [rt <<1|1] += add [rt ]; sum [rt <<1] += add [rt ] * (m - (m >> 1)); sum [rt <<1|1] += add [rt ] * (m >> 1); add [rt ] = 0; }} void build (int l,int r,int rt ) { add [rt ] = 0; if (l == r ) { scanf ("%lld",&sum [rt ]); return ; } int m = (l + r ) >> 1; build (lson ); build (rson ); PushUp (rt ); } void update (int L,int R,int c,int l,int r,int rt ) { if (L <= l && r <= R ) { add [rt ] += c ; sum [rt ] += (LL )c * (r - l + 1); return ;47484950515253545556575859606162636465666768697071727374 }PushDown(rt , r - l +1);int m =(l + r)>>1;if(L <= m) update(L , R , c , lson); if(m < R) update(L , R , c , rson); PushUp(rt);}LL query(int L,int R,int l,int r,int rt){if(L <= l && r <= R){return sum[rt];}PushDown(rt , r - l +1);int m =(l + r)>>1;LL ret =0;if(L <= m) ret += query(L , R , lson);if(m < R) ret += query(L , R , rson);return ret;}int main(){int N , Q;scanf("%d%d",&N,&Q);build(1 , N , 1);while(Q --){char op[2];int a , b , c;scanf("%s",op);if(op[0]=='Q'){scanf("%d%d",&a,&b);printf("%lld\n",query(a , b , 1 , N , 1));}else{scanf("%d%d%d",&a,&b,&c);update(a , b , c , 1 , N , 1);}}return0;}o poj2528 Mayor’s posters题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报思路:这题数据范围很大,直接搞超时+超内存,需要离散化:离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)给出下面两个简单的例子应该能体现普通离散化的缺陷:1-10 1-4 5-101-10 1-4 6-10为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.线段树功能:update:成段替换 query:简单hash?View Code CPP1 2 3 4 5 67 8 9 10 11 12 1314 15 16 17 18 1920 21 22 23 24 2526 27 28 29 30 3132 33 34 35 36 3738 39 40 41 424344 45 46 47 48 4950 51 52 53 5455 56 57 58 59 60 6162 63 #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1const int maxn = 11111; bool hash [maxn ]; int li [maxn ] , ri [maxn ]; int X [maxn *3]; int col [maxn <<4]; int cnt ; void PushDown (int rt ) { if (col [rt ] != -1) { col [rt <<1] = col [rt <<1|1] = col [rt ];col [rt ] = -1; } } void update (int L,int R,int c,int l,int r,int rt ) { if (L <= l && r <= R ) {col [rt ] = c ; return ; } PushDown (rt ); int m = (l + r ) >> 1;if (L <= m ) update (L , R , c , lson ); if (m < R ) update (L , R , c , rson ); } void query (int l,int r,int rt ) { if (col [rt ] != -1) {if (!hash [col [rt ]]) cnt ++; hash [ col [rt ] ] = true ; return ; } if (l == r ) return ; int m = (l + r ) >> 1; query (lson ); query (rson ); } int Bin (int key,int n,int X []) { int l = 0 , r = n - 1; while (l <= r ) { int m = (l + r ) >> 1; if (X [m ] == key ) return m ;if (X [m ] < key ) l = m + 1; else r = m - 1; } return -1; } int main () { int T , n ;646566676869707172737475767778798081828384 scanf("%d",&T);while(T --){scanf("%d",&n);int nn =0;for(int i =0; i < n ; i ++){scanf("%d%d",&li[i] , &ri[i]);X[nn++]= li[i];X[nn++]= ri[i];}sort(X , X + nn);int m =1;for(int i =1; i < nn; i ++){if(X[i]!= X[i-1]) X[m ++]= X[i];}for(int i = m -1; i >0; i --){if(X[i]!= X[i-1]+1) X[m ++]= X[i-1]+1;}sort(X , X + m);memset(col , -1 , sizeof(col));for(int i =0; i < n ; i ++){int l = Bin(li[i] , m , X);int r = Bin(ri[i] , m , X);update(l , r , i , 0 , m , 1);}cnt =0;memset(hash , false , sizeof(hash));query(0 , m , 1);printf("%d\n",cnt);}return0;}o poj3225 Help with Intervals题意:区间操作,交,并,补等思路:我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)U:把区间[l,r]覆盖成1I:把[-∞,l)(r,∞]覆盖成0D:把区间[l,r]覆盖成0C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换S:[l,r]区间0/1互换成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了所以当一个节点得到覆盖标记时把异或标记清空而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)线段树功能:update:成段替换,区间异或 query:简单hash?View Code CPP1 2 3 4 5 67 8 9 10 11 12 1314 15 16 17 18 1920 21 22 23 2425 26 27 28 29 3031 32 33 34 35 3637 38 39 40 414243 44 45 46 4748 49 50 51 52 5354 55 56 57 58 5960 61 62 63 64 6566 #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 131072; bool hash [maxn ]; int cover [maxn <<2]; int XOR [maxn <<2]; void FXOR (int rt ) { if (cover [rt ] != -1) cover [rt ] ^= 1; else XOR [rt ] ^= 1; } void PushDown (int rt ) { if (cover [rt ] != -1) { cover [rt <<1] = cover [rt <<1|1] = cover [rt ]; XOR [rt <<1] = XOR [rt <<1|1] = 0; cover [rt ] = -1;} if (XOR [rt ]) { FXOR (rt <<1); FXOR (rt <<1|1); XOR [rt ] = 0;} } void update (char op,int L,int R,int l,int r,int rt ) { if (L <= l && r <= R ) { if (op == 'U') {cover [rt ] = 1; XOR [rt ] = 0; } else if (op == 'D') { cover [rt ] = 0; XOR [rt ] = 0; } else if (op == 'C' || op == 'S') { FXOR (rt ); } return ;} PushDown (rt ); int m = (l + r ) >> 1; if (L <= m ) update (op , L , R , lson ); else if (op == 'I' || op == 'C') {XOR [rt <<1] = cover [rt <<1] = 0; } if (m < R ) update (op , L , R , rson ); else if (op == 'I' || op == 'C') { XOR [rt <<1|1] = cover [rt <<1|1] = 0;} } void query (int l,int r,int rt ) { if (cover [rt ] == 1) { for (int it = l ; it <= r ; it ++) {676869707172737475767778798081828384858687888990919293949596979899hash[it]=true;}return;}else if(cover[rt]==0)return;if(l == r)return;PushDown(rt);int m =(l + r)>>1;query(lson);query(rson);}int main(){cover[1]= XOR[1]=0;char op , l , r;int a , b;while( ~scanf("%c %c%d,%d%c\n",&op , &l , &a , &b , &r)){a <<=1 ,b <<=1;if(l =='(') a ++;if(r ==')') b --;if(a > b){if(op =='C'|| op =='I'){cover[1]= XOR[1]=0;}}else update(op , a , b , 0 , maxn , 1);}query(0 , maxn , 1);bool flag =false;int s =-1 , e;for(int i =0; i <= maxn ; i ++){if(hash[i]){if(s ==-1) s = i;e = i;}else{if(s !=-1){if(flag)printf(" ");flag =true;printf("%c%d,%d%c",s&1?'(':'[' , s>>1 , (e+1)>>1 , e&1?')':']');s =-1;}}}if(!flag)printf("empty set");puts("");return0;}∙练习:o poj1436 Horizontally Visible Segmentso poj2991 Craneo Another LCISo Bracket Sequence∙区间合并这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并o poj3667 Hotel题意:1 a:询问是不是有连续长度为a 的空房间,有的话住进最左边2 a b:将[a,a+b-1]的房间清空思路:记录区间中最长的空房间线段树操作:update:区间替换 query:询问满足条件的最左断点?View Code CPP1 2 3 4 5 67 8 9 10 11 12 131415 16 17 18 1920 21 22 23 24 2526 27 28 29 30 3132 33 34 35 36 3738 39 40 41 4243 44 45 46 47 4849 50 51 52 53 5455 56 57 58 59#include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 55555; int lsum [maxn <<2] , rsum [maxn <<2] , msum [maxn <<2]; int cover [maxn <<2]; void PushDown (int rt,int m ) { if (cover [rt ] != -1) { cover [rt <<1] = cover [rt <<1|1] = cover [rt ]; msum [rt <<1] = lsum [rt <<1] = rsum [rt <<1] = cover [rt ] ? 0 : m - (m >> 1);msum [rt <<1|1] = lsum [rt <<1|1] = rsum [rt <<1|1] = cover [rt ] ? 0 : (m >> 1); cover [rt ] = -1; } }void PushUp (int rt,int m ) { lsum [rt ] = lsum [rt <<1]; rsum [rt ] = rsum [rt <<1|1]; if (lsum [rt ] == m - (m >> 1)) lsum [rt ] += lsum [rt <<1|1]; if (rsum [rt ] == (m >> 1)) rsum [rt ] += rsum [rt <<1];msum [rt ] = max (lsum [rt <<1|1] + rsum [rt <<1] , max (msum [rt <<1] , msum [rt <<1|1])); } void build (int l,int r,int rt ) { msum [rt ] = lsum [rt ] = rsum [rt ] = r - l + 1; cover [rt ] = -1; if (l == r ) return ; int m = (l + r ) >> 1; build (lson );build (rson ); } void update (int L,int R,int c,int l,int r,int rt ) { if (L <= l && r <= R ) { msum [rt ] = lsum [rt ] = rsum [rt ] = c ? 0 : r - l + 1; cover [rt ] = c ; return ; } PushDown (rt , r - l + 1); int m = (l + r ) >> 1;if (L <= m ) update (L , R , c , lson ); if (m < R ) update (L , R , c , rson ); PushUp (rt , r - l + 1);60 61 62 63 6465 66 67 68 69 7071 72 73 74 757677 } int query (int w,int l,int r,int rt ) { if (l == r ) return l ; PushDown (rt , r - l + 1); int m = (l + r ) >> 1;if (msum [rt <<1] >= w ) return query (w , lson ); else if (rsum [rt <<1] + lsum [rt <<1|1] >= w ) return m - rsum [rt <<1] + 1; return query (w , rson ); }int main () { int n , m ; scanf ("%d%d",&n,&m ); build (1 , n , 1); while (m --) {int op , a , b ;scanf ("%d",&op );if (op == 1) {scanf ("%d",&a );if (msum [1] < a ) puts ("0");else {int p = query (a , 1 , n , 1);printf ("%d \n ",p );update (p , p + a - 1 , 1 , 1 , n ,1);}} else {scanf ("%d%d",&a,&b );update (a , a + b - 1 , 0 , 1 , n , 1); }}return 0;}∙ 练习:ohdu3308 LCIS ohdu3397 Sequence operation ohdu2871 Memory Control ohdu1540 Tunnel Warfare o CF46-D Parking Lot∙ 扫描线这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去 最典型的就是矩形面积并,周长并等题o hdu1542 Atlantis题意:矩形面积并思路:浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt 表示该区间下边比上边多几个线段树操作:update:区间增减 query:直接取根节点的值?View Code CPP1 2 3 4 #include <cstdio> #include <cstring> #include <cctype> #include <algorithm>5 6 7 8 9 1011 12 13 14 15 1617 18 19 20 212223 24 25 26 2728 29 30 31 32 3334 35 36 37 38 3940 41 42 43 44 4546 47 48 49 505152 53 54 55 56 5758 59 60 61 6263 64 65 66 67 6869 70 71 72using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 2222; int cnt [maxn << 2];double sum [maxn << 2]; double X [maxn ]; struct Seg { double h , l , r ; int s ; Seg (){} Seg (double a,double b,double c,int d ) : l (a ) , r (b ) , h (c ) , s (d ) {} bool operator < (const Seg &cmp ) const { return h < cmp.h ; } }ss [maxn ]; void PushUp (int rt,int l,int r ) { if (cnt [rt ]) sum [rt ] = X [r +1] - X [l ]; else if (l == r ) sum [rt ] = 0;else sum [rt ] = sum [rt <<1] + sum [rt <<1|1]; } void update (int L,int R,int c,int l,int r,int rt ) { if (L <= l && r <= R ) { cnt [rt ] += c ;PushUp (rt , l , r ); return ; } int m = (l + r ) >> 1; if (L <= m ) update (L , R , c , lson );if (m < R ) update (L , R , c , rson ); PushUp (rt , l , r ); } int Bin (double key,int n,double X []) { int l = 0 , r = n - 1; while (l <= r ) { int m = (l + r ) >> 1; if (X [m ] == key ) return m ; if (X [m ] < key ) l = m + 1; else r = m - 1; } return -1; } int main () { int n , cas = 1; while (~scanf ("%d",&n ) && n ) { int m = 0; while (n --) { double a , b , c , d ;scanf ("%lf%lf%lf%lf",&a,&b,&c,&d ); X [m ] = a ; ss [m ++] = Seg (a , c , b , 1); X [m ] = c ; ss [m ++] = Seg (a , c , d , -1); } sort (X , X + m );73 74 75 76 7778 sort (ss , ss + m ); int k = 1; for (int i = 1 ; i < m ; i ++) { if (X [i ] != X [i -1]) X [k ++] = X [i ];}memset (cnt , 0 , sizeof (cnt ));memset (sum , 0 , sizeof (sum ));double ret = 0;for (int i = 0 ; i < m - 1 ; i ++) {int l = Bin (ss [i ].l , k , X );int r = Bin (ss [i ].r , k , X ) - 1;if (l <= r ) update (l , r , ss [i ].s , 0 , k - 1, 1);ret += sum [1] * (ss [i +1].h - ss [i ].h );}printf ("Test case #%d \n Total explored area: %.2lf \n\n ",cas ++ ,ret );}return 0; } o hdu1828 Picture题意:矩形周长并思路:与面积不同的地方是还要记录竖的边有几个(numseg 记录),并且当边界重合的时候需要合并(用lbd 和rbd 表示边界来辅助)线段树操作:update:区间增减 query:直接取根节点的值?View Code CPP1 2 3 4 5 67 8 9 10 11 121314 15 16 17 1819 20 21 22 23 2425 26 27 28 293031 32 33 34 35#include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std ; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 22222; struct Seg { int l , r , h , s ; Seg () {} Seg (int a,int b,int c,int d ):l (a ) , r (b ) , h (c ) , s (d ) {} bool operator < (const Seg &cmp ) const { if (h == cmp.h ) return s > cmp.s ; return h < cmp.h ;} }ss [maxn ]; bool lbd [maxn <<2] , rbd [maxn <<2]; int numseg [maxn <<2]; int cnt [maxn <<2];int len [maxn <<2]; void PushUP (int rt,int l,int r ) { if (cnt [rt ]) { lbd [rt ] = rbd [rt ] = 1; len [rt ] = r - l + 1; numseg [rt ] = 2; } else if (l == r ) { len [rt ] = numseg [rt ] = lbd [rt ] = rbd [rt ] = 0;36 37 38 39 4041 42 43 44 45 4647 48 49 50 5152 53 54 55 56 57 5859 60 61 62 636465 66 67 68 6970 71 72 73 } else { lbd [rt ] = lbd [rt <<1]; rbd [rt ] = rbd [rt <<1|1]; len [rt ] = len [rt <<1] + len [rt <<1|1]; numseg [rt ] = numseg [rt <<1] + numseg [rt <<1|1];if (lbd [rt <<1|1] && rbd [rt <<1]) numseg [rt ] -= 2;//两条线重合 } } void update (int L,int R,int c,int l,int r,int rt ) { if (L <= l && r <= R ) {cnt [rt ] += c ; PushUP (rt , l , r ); return ; } int m = (l + r ) >> 1;if (L <= m ) update (L , R , c , lson ); if (m < R ) update (L , R , c , rson ); PushUP (rt , l , r ); } int main () { int n ; while (~scanf ("%d",&n )) { int m = 0; int lbd = 10000, rbd = -10000; for (int i = 0 ; i < n ; i ++) { int a , b , c , d ; scanf ("%d%d%d%d",&a,&b,&c,&d ); lbd = min (lbd , a ); rbd = max (rbd , c );ss [m ++] = Seg (a , c , b , 1); ss [m ++] = Seg (a , c , d , -1); }sort (ss , ss + m );int ret = 0 , last = 0;for (int i = 0 ; i < m ; i ++) {if (ss [i ].l < ss [i ].r ) update (ss [i ].l , ss [i ].r - 1 , ss [i ].s , lbd , rbd - 1 , 1);ret += numseg [1] * (ss [i +1].h - ss [i ].h ); ret += abs (len [1] - last );last = len [1];}printf ("%d \n ",ret );}return 0;}练习线段树与其他结合练习(欢迎大家补充):。