2016HUAS_ACM暑假集训2D - 敌兵布阵

      刚开始接触线段树,不得不说,每次接触到一个新的数据结构,都会是一场头脑风暴的“盛宴”。希望我能继续痛苦并快乐着学下去。我相信,有各路大神的博客相助,我还是能坚持下去的。

      这个题目是HDU的1166,只是题意改了一下(把士兵改为花的美观值了),实际上是一模一样的。用模拟的话妥妥的超时,别问我怎么知道的,哎,心累。线段树,我觉得最经典的也是最难的地方就是数据的更新,当你每次进行修改操作时,你要改动那个节点的所有父节点上的值,这点尤其重要!

      关于线段树的一个重要的关系:如果上个节点是[a,b],则它的左子树为[a,(a+b)/2],右子树为[(a+b)/2+1,b]。顺便一提的就是线段树上的每个节点都是线段。听起来挺有趣的哈......

大致题意是这样的:有四种操作,它们的形式是这样的:(i和j为正整数)

 

1.Add i j,        表示第i个数增加j(j<=30)

2.Sub i j,        表示第i个数减少j(j<=30)

3.Query i j,      i<=j,表示询问第i个数到第j的数的和

4.End,  表示结束,这条命令在每组数据最后出现

 

下面给出样例输入输出:(第一个数T表示有T数,第二个数N表示有个数,每次遇到询问Query时输出一下结果)

Sample Input
1
9
7 9 8 4 4 5 4 2 7
Query 7 9
Add 4 9
Query 3 6
Sub 9 6
Sub 3 3
Query 1 9
End
 
    
Sample Output(对于每i组,首先输出一个Case i:)
Case 1:
13
30
50

下面就贴代码吧,老规矩,思路在注释里。

  1 #include
  2 #include<string.h>
  3 
  4 #define SIZE 50010//存储N个花盆的美观值
  5 #define MAXN 150000//存储树节点
  6 
  7 int T,N,ans,x,y,n[SIZE];
  8 char p[10];
  9 tree t[MAXN];
 10 
 11 struct tree
 12 {
 13     int a,b,s;//线段树每个节点的左端点a,右端点b,以及[a,b]的总美观值
 14 };
 15 
 16 void Init(int x,int y,int z)//构造线段树
 17 {
 18     if(t[z].a==t[z].b)//叶子节点([1,1],[2,2]...)的形式
 19         t[z].s=n[y];
 20     else
 21     {
 22         Init(x,(x+y)/2,2*z);//构造左子树
 23         Init((x+y)/2+1,y,2*z+1);//构造右子树
 24         t[z].s=t[2*z].s+t[2*z+1].s;//父节点上的总美观值=两子树美观值的和
 25     }
 26 }
 27 //变为2*z的原因是:一个二叉树第n层的第一个节点的编号是第(n-1)层的第一个节点的两倍(1,2,4,8....)
 28 
 29 void Add(int x,int y,int z)//区间修改Add
 30 {
 31     t[z].s+=y;//数据更新的关键所在:从根节点往下更新,更新的原则是线段只要包含了点x,则要加上更新量y
 32     if(t[z].a==x && t[z].b==x)//更新到子节点了([x,x]那片叶子),停止
 33         return ;
 34     if(x>(t[z].a+t[z].b)/2)//如果该端点x在线段的右边,更新右子树
 35 //如果上个节点是[a,b],则它的左子树为[a,(a+b)/2],右子树为[(a+b)/2+1,b]  这里非常重要(=+-__+=)!!!
 36         Add(x,y,2*z+1);
 37     else
 38         Add(x,y,2*z);//否则更新左子树
 39 }
 40 void Sub(int x,int y,int z)//区间修改Sub 跟Add差不多的思路
 41 {
 42     t[z].s-=y;
 43     if(t[z].a==x&&t[z].b==x)
 44         return ;
 45     if(x>(t[z].a+t[z].b)/2)
 46         Sub(x,y,2*z+1);
 47     else
 48         Sub(x,y,2*z);
 49 }
 50 
 51 void Query(int x,int y,int z)//区间查询Qyery
 52 {
 53     if(t[z].a>=x&&t[z].b<=y)//[x,y]刚好在[a,b]内
 54         ans+=t[z].s;//记录答案
 55     else
 56     {
 57         if(x>(t[z].a+t[z].b)/2)//[x,y]在右子树上
 58             Query(x,y,2*z+1);
 59         else if(y<=(t[z].a+t[z].b)/2)//在左子树上
 60             Query(x,y,2*z);
 61         else//如果[x,y]在两个子树上都有,都查就行了
 62         {
 63             Query(x,y,2*z);
 64             Query(x,y,2*z+1);
 65         }
 66     }
 67 }
 68 //这里体现的线段树的优越性,在查询的时候不需要全部遍历
 69 int main()
 70 {
 71     int i,j;
 72     scanf("%d",&T);
 73     for(i=1;i<=T;i++)
 74     {
 75         scanf("%d",&N);
 76         for(j=1;j<=N;j++)
 77             scanf("%d",&n[j]);
 78         Init(1,N,1); 
 79         printf("Case %d:\n",i);
 80         while(scanf("%s",&p),strcmp(p,"End"))
 81         {
 82             if(strcmp(p,"Add")==0)//Add  第x个数加y
 83             {
 84                 scanf("%d %d",&x,&y);
 85                 Add(x,y,1);
 86             }
 87             else if((strcmp(p,"Sub")==0))//Sub  第x个数减y
 88             {
 89                 scanf("%d %d",&x,&y);
 90                 Sub(x,y,1);
 91             }
 92             else if((strcmp(p,"Query")==0))//Query 输出x->y的美观值
 93             {
 94                 ans=0;
 95                 scanf("%d %d",&x,&y);
 96                 Query(x,y,1);
 97                 printf("%d\n",ans);
 98             }    
 99         }
100     }
101     return 0;
102 }
View Code

哦,这里还有一个坑,如果用C++的cin和cout的话,光荣的TLE......小伙伴们不信可以去试试。如果不TLE,希望大神能给我分享一下你的code,感激不尽。

转载于:https://www.cnblogs.com/ankelen/p/5691187.html

你可能感兴趣的:(2016HUAS_ACM暑假集训2D - 敌兵布阵)