描述
校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
K=1,K=1,读入l、r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同
K=2,读入l,r表示询问l~r之间能见到多少种树(l,r>0)
输入格式
第一行n,m表示道路总长为n,共有m个操作
接下来m行为m个操作
输出格式
对于每个k=2输出一个答案
样例输入
5 4
1 1 3
2 2 5
1 2 4
2 3 5
样例输出
1
2
普通的暴力算法,植树的时间为O(n),查询的时间也为O(n),所以总体的时间复杂度为O(nm)。
这里介绍一种独特的想法---括号法。
在植树区间的左端点放一个左括号“(”,右端点放一个右括号“)”,使得植树时间为O(1)。
显而易见,查询的结果为右端点左边“(”的个数减去左端点左边“)”的个数,时间为O(n)。
2至5之间有2-1=1种树。
3至5之间有2-0=2种树。
为了进一步优化时间复杂度,我们使用线段树维护左右括号的数量,使时间降到log级。
献上代码:
1 #include <cstdio> 2 int n,m; 3 struct node 4 { 5 int x,y; 6 int a[3];//a[1]表示左括号的数量,a[2]表示右括号的数量。 7 }t[300002]; 8 void init(int k,int l,int r)//初始化。 9 { 10 t[k].x=l; 11 t[k].y=r; 12 if(l==r)return; 13 int mid=(l+r)/2; 14 init(2*k,l,mid); 15 init(2*k+1,mid+1,r); 16 } 17 void build(int k,int l,int op) 18 { 19 if(t[k].x<=l&&l<=t[k].y)//若在当前节点的范围内,括号数量加1。 20 t[k].a[op]++; 21 if(t[k].x==t[k].y)return; 22 int mid=(t[k].x+t[k].y)/2; 23 if(l<=mid)build(2*k,l,op);//查询左儿子。 24 if(l>=mid+1)build(2*k+1,l,op);//查询右儿子。 25 } 26 int find(int k,int l,int r,int op)//查找l到r内括号的个数。 27 { 28 int ans=0; 29 if(l<=t[k].x&&t[k].y<=r)return t[k].a[op]; 30 int mid=(t[k].x+t[k].y)/2; 31 if(l<=mid)ans+=find(2*k,l,r,op); 32 if(r>=mid+1)ans+=find(2*k+1,l,r,op); 33 return ans; 34 } 35 int main() 36 { 37 int k,l,r,ans1,ans2; 38 scanf("%d%d",&n,&m); 39 init(1,1,n); 40 for(int i=1;i<=m;i++) 41 { 42 scanf("%d%d%d",&k,&l,&r); 43 if(k==1) 44 { 45 build(1,l,1); 46 build(1,r,2); 47 } 48 if(k==2) 49 { 50 ans1=ans2=0; 51 ans1=find(1,1,r,1); 52 if(l-1>=1)ans2=find(1,1,l-1,2); 53 printf("%d\n",ans1-ans2); 54 } 55 } 56 return 0; 57 }
时间: 2024-11-08 02:08:40