线段树专题

来源:互联网 发布:传智播客52期php下载 编辑:程序博客网 时间:2024/06/10 00:37

hdu 1166 敌兵布阵

操作:单点增加或减少,查询区间和.

http://acm.hdu.edu.cn/showproblem.php?pid=1166

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=50010;  
  8.   
  9. struct ST  
  10. {  
  11.     int l,r;  
  12.     int sum;  
  13. }st[maxn<<2];  
  14.   
  15. void pushUp(int i)  
  16. {  
  17.     st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;  
  18. }  
  19.   
  20. void build(int i,int l,int r)  
  21. {  
  22.     st[i].l=l;  
  23.     st[i].r=r;  
  24.     if(st[i].l==st[i].r)  
  25.     {  
  26.         rd(st[i].sum);  
  27.         return;  
  28.     }  
  29.     int mid=(st[i].l+st[i].r)>>1;  
  30.     build(i<<1,l,mid);  
  31.     build((i<<1)|1,mid+1,r);  
  32.     pushUp(i);  
  33. }  
  34.   
  35. void add(int i,int p,int val)  
  36. {  
  37.     if(st[i].l==st[i].r)  
  38.     {  
  39.         st[i].sum+=val;  
  40.         return;  
  41.     }  
  42.     int mid=(st[i].l+st[i].r)>>1;  
  43.     if(p<=mid)  
  44.         add(i<<1,p,val);  
  45.     else  
  46.         add((i<<1)|1,p,val);  
  47.     pushUp(i);  
  48. }  
  49.   
  50. int query(int i,int L,int R)  
  51. {  
  52.     if(st[i].l==L&&st[i].r==R)  
  53.     {  
  54.         return st[i].sum;  
  55.     }  
  56.     int mid=(st[i].l+st[i].r)>>1;  
  57.     if(R<=mid)  
  58.         return query(i<<1,L,R);  
  59.     else if(L>mid)  
  60.         return query((i<<1)|1,L,R);  
  61.     else  
  62.         return query(i<<1,L,mid)+query((i<<1)|1,mid+1,R);  
  63. }  
  64. int n;  
  65. char cm[10];  
  66.   
  67. int main()  
  68. {  
  69.     int cas=1;  
  70.     int t;rd(t);  
  71.     while(t--)  
  72.     {  
  73.         printf("Case %d:\n",cas++);  
  74.         rd(n);  
  75.         build(1,1,n);  
  76.         while(scanf("%s",cm))  
  77.         {  
  78.             if(cm[0]=='Q')  
  79.             {  
  80.                 int l,r;  
  81.                 rd2(l,r);  
  82.                 printf("%d\n",query(1,l,r));  
  83.             }  
  84.             else if(cm[0]=='A')  
  85.             {  
  86.                 int p,val;  
  87.                 rd2(p,val);  
  88.                 add(1,p,val);  
  89.             }  
  90.             else if(cm[0]=='S')  
  91.             {  
  92.                 int p,val;  
  93.                 rd2(p,val);  
  94.                 add(1,p,-val);  
  95.             }  
  96.             else  
  97.                 break;  
  98.         }  
  99.     }  
  100.     return 0;  
  101. }  

 

hdu 1754  I hate it

http://acm.hdu.edu.cn/showproblem.php?pid=1754

操作:单点替换为另一个值,查询区间最大值.

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=200010;  
  8.   
  9. struct ST  
  10. {  
  11.     int l,r;  
  12.     int MAX;  
  13. }st[maxn<<2];  
  14.   
  15. void pushUp(int i)  
  16. {  
  17.     st[i].MAX=max(st[i<<1].MAX,st[(i<<1)|1].MAX);  
  18. }  
  19.   
  20. void build(int i,int l,int r)  
  21. {  
  22.     st[i].l=l;  
  23.     st[i].r=r;  
  24.     if(st[i].l==st[i].r)  
  25.     {  
  26.         rd(st[i].MAX);  
  27.         return;  
  28.     }  
  29.     int mid=(st[i].l+st[i].r)>>1;  
  30.     build(i<<1,l,mid);  
  31.     build((i<<1)|1,mid+1,r);  
  32.     pushUp(i);  
  33. }  
  34.   
  35. void update(int i,int p,int val)  
  36. {  
  37.     if(st[i].l==st[i].r)  
  38.     {  
  39.         st[i].MAX=val;  
  40.         return;  
  41.     }  
  42.     int mid=(st[i].l+st[i].r)>>1;  
  43.     if(p<=mid)  
  44.         update(i<<1,p,val);  
  45.     else  
  46.         update((i<<1)|1,p,val);  
  47.     pushUp(i);  
  48. }  
  49.   
  50. int query(int i,int L,int R)  
  51. {  
  52.     if(st[i].l==L&&st[i].r==R)  
  53.     {  
  54.         return st[i].MAX;  
  55.     }  
  56.     int mid=(st[i].l+st[i].r)>>1;  
  57.     if(R<=mid)  
  58.         return query(i<<1,L,R);  
  59.     else if(L>mid)  
  60.         return query((i<<1)|1,L,R);  
  61.     else  
  62.         return max(query(i<<1,L,mid),query((i<<1)|1,mid+1,R));  
  63. }  
  64. int n,m;  
  65. char cm[5];  
  66. int main()  
  67. {  
  68.     while(rd2(n,m)!=EOF)  
  69.     {  
  70.         build(1,1,n);  
  71.         while(m--)  
  72.         {  
  73.             scanf("%s",cm);  
  74.             if(cm[0]=='Q')  
  75.             {  
  76.                 int l,r;  
  77.                 rd2(l,r);  
  78.                 printf("%d\n",query(1,l,r));  
  79.             }  
  80.             else  
  81.             {  
  82.                 int p,val;  
  83.                 rd2(p,val);  
  84.                 update(1,p,val);  
  85.             }  
  86.         }  
  87.     }  
  88.     return 0;  
  89. }  


poj 3468 A Simple Problem with Integers

http://poj.org/problem?id=3468
操作:区间的每个值都增加一个数,查询区间总和。

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int maxn=100010;  
  7.   
  8. struct ST  
  9. {  
  10.     int l,r;  
  11.     ll lazy;  
  12.     ll sum;  
  13. }st[maxn<<2];  
  14.   
  15. void pushUp(int i)  
  16. {  
  17.     st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;  
  18. }  
  19.   
  20. void pushDown(int i,int len)  
  21. {  
  22.     if(st[i].lazy!=0)  
  23.     {  
  24.         st[i<<1].lazy+=st[i].lazy;  
  25.         st[(i<<1)|1].lazy+=st[i].lazy;  
  26.         st[i<<1].sum+=(long long)(len-(len>>1))*st[i].lazy;  
  27.         st[(i<<1)|1].sum+=(long long)(len>>1)*st[i].lazy;  
  28.         st[i].lazy=0;  
  29.     }  
  30. }  
  31.   
  32. void build(int i,int l,int r)  
  33. {  
  34.     st[i].l=l;  
  35.     st[i].r=r;  
  36.     st[i].lazy=0;  
  37.     if(st[i].l==st[i].r)  
  38.     {  
  39.         scanf("%I64d",&st[i].sum);  
  40.         return;  
  41.     }  
  42.     int mid=(st[i].l+st[i].r)>>1;  
  43.     build(i<<1,l,mid);  
  44.     build((i<<1)|1,mid+1,r);  
  45.     pushUp(i);  
  46. }  
  47.   
  48. void add(int i,int L,int R,int val)  
  49. {  
  50.     if(st[i].l==L&&st[i].r==R)  
  51.     {  
  52.         st[i].sum+=(long long)(st[i].r-st[i].l+1)*val;  
  53.         st[i].lazy+=val;  
  54.         return;  
  55.     }  
  56.     pushDown(i,st[i].r-st[i].l+1);  
  57.     int mid=(st[i].l+st[i].r)>>1;  
  58.     if(R<=mid)  
  59.         add(i<<1,L,R,val);  
  60.     else if(L>mid)  
  61.         add((i<<1)|1,L,R,val);  
  62.     else  
  63.     {  
  64.         add(i<<1,L,mid,val);  
  65.         add((i<<1)|1,mid+1,R,val);  
  66.     }  
  67.     pushUp(i);  
  68. }  
  69.   
  70. ll query(int i,int L,int R)  
  71. {  
  72.     if(st[i].l==L&&st[i].r==R)  
  73.     {  
  74.         return st[i].sum;  
  75.     }  
  76.     pushDown(i,st[i].r-st[i].l+1);  
  77.     int mid=(st[i].l+st[i].r)>>1;  
  78.     if(R<=mid)  
  79.         return query(i<<1,L,R);  
  80.     else if(L>mid)  
  81.         return query((i<<1)|1,L,R);  
  82.     else  
  83.         return query(i<<1,L,mid)+query((i<<1)|1,mid+1,R);  
  84. }  
  85.   
  86. int n,q;  
  87. int l,r,val;  
  88. char cm;  
  89.   
  90. int main()  
  91. {  
  92.     while(scanf("%d%d",&n,&q)!=EOF)  
  93.     {  
  94.         build(1,1,n);  
  95.         while(q--)  
  96.         {  
  97.             scanf("%s",&cm);  
  98.             if(cm=='C')  
  99.             {  
  100.                 scanf("%d%d%d",&l,&r,&val);  
  101.                 add(1,l,r,val);  
  102.             }  
  103.             else  
  104.             {  
  105.                 scanf("%d%d",&l,&r);  
  106.                 printf("%I64d\n",query(1,l,r));  
  107.             }  
  108.         }  
  109.     }  
  110.     return 0;  
  111. }  

hdu 1698 just a hook

http://acm.hdu.edu.cn/showproblem.php?pid=1698

操作:指定区间每个值修改为另一个值,求总区间总和。

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int maxn=100010;  
  7.   
  8. struct ST  
  9. {  
  10.     int l,r;  
  11.     int lazy;  
  12.     int sum;  
  13. }st[maxn<<2];  
  14.   
  15. void pushUp(int i)  
  16. {  
  17.     st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;  
  18. }  
  19.   
  20. void pushDown(int i,int len)  
  21. {  
  22.     if(st[i].lazy!=0)  
  23.     {  
  24.         st[i<<1].lazy=st[(i<<1)|1].lazy=st[i].lazy;  
  25.         st[i<<1].sum=(len-(len>>1))*st[i].lazy;  
  26.         st[(i<<1)|1].sum=(len>>1)*st[i].lazy;  
  27.         st[i].lazy=0;  
  28.     }  
  29. }  
  30.   
  31. void build(int i,int l,int r)  
  32. {  
  33.     st[i].l=l;  
  34.     st[i].r=r;  
  35.     st[i].lazy=0;  
  36.     if(st[i].l==st[i].r)  
  37.     {  
  38.         st[i].sum=1;  
  39.         return;  
  40.     }  
  41.     int mid=(st[i].l+st[i].r)>>1;  
  42.     build(i<<1,l,mid);  
  43.     build((i<<1)|1,mid+1,r);  
  44.     pushUp(i);  
  45. }  
  46.   
  47. void update(int i,int l,int r,int val)  
  48. {  
  49.     if(st[i].l==l&&st[i].r==r)  
  50.     {  
  51.         st[i].sum=(st[i].r-st[i].l+1)*val;  
  52.         st[i].lazy=val;  
  53.         return;  
  54.     }  
  55.     pushDown(i,st[i].r-st[i].l+1);  
  56.     int mid=(st[i].l+st[i].r)>>1;  
  57.     if(r<=mid)  
  58.         update(i<<1,l,r,val);  
  59.     else if(l>mid)  
  60.         update((i<<1)|1,l,r,val);  
  61.     else  
  62.     {  
  63.         update(i<<1,l,mid,val);  
  64.         update((i<<1)|1,mid+1,r,val);  
  65.     }  
  66.     pushUp(i);  
  67. }  
  68.   
  69. int t,n,q;  
  70. int l,r,val;  
  71. int cas=1;  
  72.   
  73. int main()  
  74. {  
  75.     rd(t);  
  76.     while(t--)  
  77.     {  
  78.         rd(n);  
  79.         rd(q);  
  80.         build(1,1,n);  
  81.         while(q--)  
  82.         {  
  83.             rd3(l,r,val);  
  84.             update(1,l,r,val);  
  85.         }  
  86.         printf("Case %d: The total value of the hook is %d.\n",cas++,st[1].sum);  
  87.     }  
  88.   
  89.     return 0;  
  90. }  


poj 2528 Mayor's posters

http://poj.org/problem?id=2528

有一条1到10000000的线段,然后给指定区间涂颜色,制定区间个数最多为10000个,后面的颜色覆盖前面的颜色,问最后涂完一共可以看见几种颜色。

要用到离散化,比如 [1, 10000]  [2,  99999999]   [10000,20000]这三个区间

端点从小到大排序并去重后得到

1  2 10000 20000 99999999    分别映射到数字 1 2 3 4  5,也就是1->1   2->2    10000->3   20000->4   99999999->5

那么给定的区间就可以变为[1,3]  [2,5]  [3,4] ,原覆盖关系没变,这样区间长度就大大缩短了

但这样容易出错 比如  [1,10]   [1,4]  [6,10]  这组数据,答案应该是3, 但是像前面那样处理后,区间变为[1,4]   [1,2]   [3,4] ,答案是2,处理方法为

端点从小到大排序并去重得到1 4 6 10 ,对于相邻两个数如果差距大于1,那么就再里面随便加上一个数,变为 1 (2) 4 (5) 6 (7)  10

然后映射关系为1->1   2->2  4->3  5->4   6->5   7->6  10->7

那么原区间就变为[1,7]  [ 1,3]   [5,7]   ,答案正确为3

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=10010;  
  8. int id[10000005];//离散化以后对应的id,原区间为l,r,离散化后变为id[l],id[r]  
  9. int t;  
  10. int n;  
  11. int x[maxn*3];//4倍的,2倍是一条线段两个端点,1倍是两个端点之间再加一个,就像[1,10] [1,4] [6,10]这样的数据防止错误  
  12.   
  13. struct ST  
  14. {  
  15.     int l,r;  
  16.     int covered;  
  17. }st[maxn*16];//要开线段长度的4倍,按理说12就可以,但是re,开了13就可以,迷糊.....  
  18.   
  19. struct poster  
  20. {  
  21.     int l,r;  
  22. }post[maxn];  
  23.   
  24. void build(int i,int l,int r)  
  25. {  
  26.     st[i].l=l;  
  27.     st[i].r=r;  
  28.     st[i].covered=false;//一开始都是没被覆盖  
  29.     if(st[i].l==st[i].r)  
  30.         return;  
  31.     int mid=(st[i].l+st[i].r)>>1;  
  32.     build(i<<1,l,mid);  
  33.     build((i<<1)|1,mid+1,r);  
  34. }  
  35.   
  36. bool isvisible(int i,int l,int r)  
  37. {  
  38.     if(st[i].covered==true)//如果该节点代表的区间已被覆盖掉,直接返回  
  39.         return false;  
  40.     if(st[i].l==l&&st[i].r==r)//找到那一段区间  
  41.     {  
  42.         st[i].covered=true;  
  43.         return true;  
  44.     }  
  45.     bool ok;  
  46.     int mid=(st[i].l+st[i].r)>>1;  
  47.     if(r<=mid)  
  48.         ok=isvisible(i<<1,l,r);  
  49.     else if(l>mid)  
  50.         ok=isvisible((i<<1)|1,l,r);  
  51.     else  
  52.     {  
  53.         bool a=isvisible(i<<1,l,mid);//左一半区间是否被覆盖  
  54.         bool b=isvisible((i<<1)|1,mid+1,r);//有一半  
  55.         ok=a||b;//有一个为真,Ok就为真,为真代表没有被覆盖  
  56.     }  
  57.     if(st[i<<1].covered&&st[(i<<1)|1].covered)//向上更新  
  58.         st[i].covered=true;  
  59.     return ok;  
  60. }  
  61.   
  62.   
  63. int main()  
  64. {  
  65.     rd(t);  
  66.     while(t--)  
  67.     {  
  68.         rd(n);  
  69.         int len=0;  
  70.         for(int i=1;i<=n;i++)  
  71.         {  
  72.             rd2(post[i].l,post[i].r);  
  73.             x[len++]=post[i].l;  
  74.             x[len++]=post[i].r;  
  75.         }  
  76.         sort(x,x+len);  
  77.         len=unique(x,x+len)-x;//去掉重复  
  78.         int tp=len;  
  79.         for(int i=1;i<tp;i++)  
  80.         {  
  81.             if(x[i]-x[i-1]>1)  
  82.                 x[len++]=x[i-1]+1;//避免 [1,10]  [1,4] [6,10]这样的数据  
  83.         }  
  84.         sort(x,x+len);  
  85.         for(int i=0;i<len;i++)  
  86.         {  
  87.             id[x[i]]=i+1;  
  88.         }  
  89.         build(1,1,len);  
  90.         int ans=0;  
  91.         for(int i=n;i>=1;i--)  
  92.         {  
  93.             if(isvisible(1,id[post[i].l],id[post[i].r]))  
  94.                 ans++;  
  95.         }  
  96.         printf("%d\n",ans);  
  97.     }  
  98.     return 0;  
  99. }  


zoj 1610 Count the Colors

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1610

在一条线段上染色,颜色的种类用数字表示,后染色的覆盖前面的染色,问最后能看到几种颜色,且每种颜色有多少不连续的段。比如 [1,8]先染成白色,[1,2]再染成红色,[5,8]再染成红色,那么可以看到白色的有一段(中间),红色的有两段。    题目中所有涉及的数的范围都为[0,8000]。

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=8010;  
  8. int color[maxn];//每个点的颜色  
  9. int ans[maxn];//输出的  
  10.   
  11. struct ST  
  12. {  
  13.     int l,r;  
  14.     int flag;//代表的颜色,默认-1  
  15. }st[maxn<<2];  
  16.   
  17. void pushUp(int i)  
  18. {  
  19.     if(st[i<<1].flag==st[(i<<1)|1].flag&&st[i<<1].flag!=-1)  
  20.         st[i].flag=st[i<<1].flag;  
  21. }  
  22.   
  23. void pushDown(int i)  
  24. {  
  25.     if(st[i].flag!=-1)  
  26.     {  
  27.         st[i<<1].flag=st[i].flag;  
  28.         st[(i<<1)|1].flag=st[i].flag;  
  29.         st[i].flag=-1;//这里是-1,其实是混合色  
  30.     }  
  31. }  
  32.   
  33. void build(int i,int l,int r)  
  34. {  
  35.     st[i].l=l;  
  36.     st[i].r=r;  
  37.     st[i].flag=-1;  
  38.     if(st[i].l==st[i].r)  
  39.         return;  
  40.     int mid=(st[i].l+st[i].r)>>1;  
  41.     build(i<<1,l,mid);  
  42.     build((i<<1)|1,mid+1,r);  
  43. }  
  44.   
  45. void update(int i,int l,int r,int val)  
  46. {  
  47.     if(st[i].flag==val)  
  48.         return;  
  49.     if(l<=st[i].l&&st[i].r<=r)//因为参数l,r是整个染色的区间,下面的递归也是整个染色的区间,用[1,9]建立线段树,给[1,8]染色模拟一遍就懂了  
  50.     {  
  51.         st[i].flag=val;  
  52.         return;  
  53.     }  
  54.     pushDown(i);  
  55.     int mid=(st[i].l+st[i].r)>>1;  
  56.     if(r<=mid)  
  57.         update(i<<1,l,r,val);  
  58.     else if(l>mid)  
  59.         update((i<<1)|1,l,r,val);  
  60.     else  
  61.     {  
  62.         update(i<<1,l,r,val);  
  63.         update((i<<1)|1,l,r,val);  
  64.     }  
  65.     pushUp(i);  
  66. }  
  67.   
  68. void query(int i)  
  69. {  
  70.     if(st[i].flag!=-1)  
  71.     {  
  72.         for(int j=st[i].l;j<=st[i].r;j++)  
  73.             color[j]=st[i].flag;  
  74.         return;  
  75.     }  
  76.     if(st[i].l==st[i].r)//父节点的flag-1,那么子节点的flag有可能-1,也有可能不是-1,加这一句话是-1的情况,没有染色  
  77.         return;  
  78.     query(i<<1);  
  79.     query((i<<1)|1);  
  80. }  
  81.   
  82. int n,N=8000;  
  83. int l,r,val;  
  84.   
  85. int main()  
  86. {  
  87.     while(rd(n)!=EOF)  
  88.     {  
  89.         build(1,0,N);  
  90.         memset(color,-1,sizeof(color));  
  91.         memset(ans,0,sizeof(ans));  
  92.         while(n--)  
  93.         {  
  94.             rd3(l,r,val);  
  95.             update(1,l,r-1,val);//为了避免 [1,2] [3,4]这样的情况,如果是相同的颜色,那么应该是两段[2,3]是空白  
  96.         }  
  97.         query(1);  
  98.         int pre=-1;  
  99.         for(int i=0;i<N;i++)  
  100.         {  
  101.             if(pre!=color[i])  
  102.             {  
  103.                 pre=color[i];  
  104.                 if(pre==-1)  
  105.                     continue;  
  106.                 ans[color[i]]++;  
  107.             }  
  108.         }  
  109.         for(int i=0;i<N;i++)  
  110.             if(ans[i])  
  111.             printf("%d %d\n",i,ans[i]);  
  112.         printf("\n");  
  113.     }  
  114.     return 0;  
  115. }  


poj 3264 Balanced Lineup

http://poj.org/problem?id=3264

求区间内最大值与最小值的差。

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=50010;  
  8.   
  9. struct ST  
  10. {  
  11.     int l,r;  
  12.     int MAX,MIN;  
  13. }st[maxn<<2];  
  14.   
  15. void pushUp(int i)  
  16. {  
  17.     st[i].MAX=max(st[i<<1].MAX,st[(i<<1)|1].MAX);  
  18.     st[i].MIN=min(st[i<<1].MIN,st[(i<<1)|1].MIN);  
  19. }  
  20.   
  21. void build(int i,int l,int r)  
  22. {  
  23.     st[i].l=l;  
  24.     st[i].r=r;  
  25.     if(st[i].l==st[i].r)  
  26.     {  
  27.         rd(st[i].MAX);  
  28.         st[i].MIN=st[i].MAX;  
  29.         return;  
  30.     }  
  31.     int mid=(st[i].l+st[i].r)>>1;  
  32.     build(i<<1,l,mid);  
  33.     build((i<<1)|1,mid+1,r);  
  34.     pushUp(i);  
  35. }  
  36.   
  37. int query1(int i,int l,int r)  
  38. {  
  39.     if(st[i].l==l&&st[i].r==r)  
  40.     {  
  41.         return st[i].MAX;  
  42.     }  
  43.     int mid=(st[i].l+st[i].r)>>1;  
  44.     if(r<=mid)  
  45.         return query1(i<<1,l,r);  
  46.     else if(l>mid)  
  47.         return query1((i<<1)|1,l,r);  
  48.     else  
  49.     {  
  50.         return max(query1(i<<1,l,mid),query1((i<<1)|1,mid+1,r));  
  51.     }  
  52. }  
  53.   
  54. int query2(int i,int l,int r)  
  55. {  
  56.     if(st[i].l==l&&st[i].r==r)  
  57.     {  
  58.         return st[i].MIN;  
  59.     }  
  60.     int mid=(st[i].l+st[i].r)>>1;  
  61.     if(r<=mid)  
  62.         return query2(i<<1,l,r);  
  63.     else if(l>mid)  
  64.         return query2((i<<1)|1,l,r);  
  65.     else  
  66.     {  
  67.         return min(query2(i<<1,l,mid),query2((i<<1)|1,mid+1,r));  
  68.     }  
  69. }  
  70.   
  71. int MAX,MIN;  
  72. void query(int i,int l,int r)  
  73. {  
  74.     if(st[i].MAX<MAX&&st[i].MIN>MIN)  
  75.         return;  
  76.     if(st[i].l==l&&st[i].r==r)  
  77.     {  
  78.         MAX=max(st[i].MAX,MAX);  
  79.         MIN=min(st[i].MIN,MIN);  
  80.         return;  
  81.     }  
  82.     int mid=(st[i].l+st[i].r)>>1;  
  83.     if(r<=mid)  
  84.         query(i<<1,l,r);  
  85.     else if(l>mid)  
  86.         query((i<<1)|1,l,r);  
  87.     else  
  88.     {  
  89.         query(i<<1,l,mid);  
  90.         query((i<<1)|1,mid+1,r);  
  91.     }  
  92. }  
  93.   
  94. int n,q;  
  95. int l,r;  
  96.   
  97. int main()  
  98. {  
  99.     while(rd2(n,q)!=EOF)  
  100.     {  
  101.         build(1,1,n);  
  102.         while(q--)  
  103.         {  
  104.             rd2(l,r);  
  105.              MAX=-1,MIN=1000002;  
  106.            // printf("%d\n",query1(1,l,r)-query2(1,l,r));这个也可以  
  107.            query(1,l,r);  
  108.            printf("%d\n",MAX-MIN);  
  109.         }  
  110.     }  
  111.     return 0;  
  112. }  


hdu 4027 Can you answer these queries?

http://acm.hdu.edu.cn/showproblem.php?pid=4027

操作:给定区间,将区间内的每个值都变为原来的平方根(去整),查询区间和。long long

当一个数为0或者为1时,该数就不用再被更新了,用allone表示该数或者该区间是否需要更新,当allone==1是,表示不用更新,碰到该节点,直接返回就可以。

题目给出的区间端点x,y,没有说明x<=y,得判断一下.....

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=100010;  
  8.   
  9. struct ST  
  10. {  
  11.     int l,r;  
  12.     ll sum;  
  13.     bool allone;  
  14. }st[maxn<<2];  
  15.   
  16. void pushUp(int i)  
  17. {  
  18.     st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;  
  19.     st[i].allone=st[i<<1].allone&&st[(i<<1)|1].allone;  
  20. }  
  21.   
  22. void build(int i,int l,int r)  
  23. {  
  24.     st[i].l=l;  
  25.     st[i].r=r;  
  26.     if(st[i].l==st[i].r)  
  27.     {  
  28.         scanf("%I64d",&st[i].sum);  
  29.         if(st[i].sum==0||st[i].sum==1)  
  30.             st[i].allone=1;  
  31.         else  
  32.             st[i].allone=0;  
  33.         return;  
  34.     }  
  35.     int mid=(st[i].l+st[i].r)>>1;  
  36.     build(i<<1,l,mid);  
  37.     build((i<<1)|1,mid+1,r);  
  38.     pushUp(i);  
  39. }  
  40.   
  41. void cal(int i,int l,int r)  
  42. {  
  43.     //if(st[i].sum==(st[i].r-st[i].l+1))//数据中如果每个endurance值不为0的话可以这样判断,这个区间每个点都为1,就不用再更新  
  44.        // return;  
  45.     if(st[i].allone==1)//该区间内全是1,不用再更新了  
  46.         return;  
  47.     if(st[i].l==st[i].r)//找到该数  
  48.     {  
  49.         st[i].sum=(ll)sqrt(1.0*st[i].sum);  
  50.         if(st[i].sum==1)  
  51.             st[i].allone=1;  
  52.         return;  
  53.     }  
  54.     int mid=(st[i].l+st[i].r)>>1;  
  55.     if(r<=mid)  
  56.         cal(i<<1,l,r);  
  57.     else if(l>mid)  
  58.         cal((i<<1)|1,l,r);  
  59.     else  
  60.     {  
  61.         cal(i<<1,l,mid);  
  62.         cal((i<<1)|1,mid+1,r);  
  63.     }  
  64.     pushUp(i);  
  65. }  
  66.   
  67. ll query(int i,int l,int r)  
  68. {  
  69.     if(st[i].l==l&&st[i].r==r)  
  70.     {  
  71.         return st[i].sum;  
  72.     }  
  73.     int mid=(st[i].l+st[i].r)>>1;  
  74.     if(r<=mid)  
  75.         return query(i<<1,l,r);  
  76.     else if(l>mid)  
  77.         return query((i<<1)|1,l,r);  
  78.     else  
  79.     {  
  80.         return query(i<<1,l,mid)+query((i<<1)|1,mid+1,r);  
  81.     }  
  82. }  
  83.   
  84. int n,q;  
  85. int cm,l,r;  
  86. int c=1;  
  87.   
  88. int main()  
  89. {  
  90.     while(rd(n)!=EOF)  
  91.     {  
  92.         printf("Case #%d:\n",c++);  
  93.         build(1,1,n);  
  94.         rd(q);  
  95.         while(q--)  
  96.         {  
  97.             rd3(cm,l,r);  
  98.             if(l>r)  
  99.                 swap(l,r);//陷阱!  
  100.             if(!cm)  
  101.             {  
  102.                 cal(1,l,r);  
  103.             }  
  104.             else  
  105.                 printf("%I64d\n",query(1,l,r));  
  106.         }  
  107.         printf("\n");  
  108.     }  
  109.     return 0;  
  110. }  


poj 2892  Tunnel Warfare

http://poj.org/problem?id=2892

一开始有连续的n个1,位置分别是1-n,三种操作,一是将某个位置的1变为0,而是将某个位置的0修复为1,三是查询包含某个位置的最大连续区间(该区间内都是1)的长度.

样例

7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4

一开始7个位置全是1,   1 1 1 1 1 1 1, D 3表示将第3个位置设为0,即 1 1 0 1 1 1 1,D 6, 1 1 0 1 1 0 1,D 5 ,   1 1 0 1 0 0 1, 

Q 4表示查询包含位置4的最大连续区间(全为1), 答案为1,Q 5答案为0, 都是在 1 1 0 1 0 0 1中看出来的

R表示将最后一个设为0的位置修复为1,也就是将第5个位置修复为1 , 即  1 1 0 1 1 0 1,Q 4 ,答案为2

再一个R表示将第6个位置修复为1,1 1 0 1 1 1 1,Q4, 答案为4

想法在注释中。

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=50010;  
  8.   
  9. struct ST  
  10. {  
  11.     int l,r;  
  12.     int ls,rs,ms;//区间左端多少个连续1,右端多少个连续1,该区间最大连续1长多少,即左端连续区间,右端连续区间,最大连续区间  
  13. }st[maxn<<2];  
  14.   
  15. void build(int i,int l,int r)  
  16. {  
  17.     st[i].l=l;  
  18.     st[i].r=r;  
  19.     if(st[i].l==st[i].r)  
  20.     {  
  21.         st[i].ls=1;st[i].rs=1;st[i].ms=1;  
  22.         return;  
  23.     }  
  24.     int mid=(st[i].l+st[i].r)>>1;  
  25.     build(i<<1,l,mid);  
  26.     build((i<<1)|1,mid+1,r);  
  27.     st[i].ls=st[i].rs=st[i].ms=st[i].r-st[i].l+1;  
  28. }  
  29.   
  30. void update(int i,int p,char c)//位置为p,命令为c  
  31. {  
  32.     if(st[i].l==st[i].r)  
  33.     {  
  34.         if(c=='D')//破坏  
  35.             st[i].ls=st[i].rs=st[i].ms=0;  
  36.         else//修复  
  37.             st[i].ls=st[i].rs=st[i].ms=1;  
  38.         return;  
  39.     }  
  40.     int mid=(st[i].l+st[i].r)>>1;  
  41.     if(p<=mid)  
  42.         update(i<<1,p,c);  
  43.     else  
  44.         update((i<<1)|1,p,c);  
  45.     st[i].ls=st[i<<1].ls;//孩子节点左连续区间肯定是父节点的左端连续区间的一部分  
  46.     st[i].rs=st[(i<<1)|1].rs;//右连续区间  
  47.     st[i].ms=max(max(st[i<<1].ms,st[(i<<1)|1].ms),st[i<<1].rs+st[(i<<1)|1].ls);  
  48.     //父亲节点的最大连续区间为  左孩子最大连续区间,右孩子最大连续区间,中间连续最大区间三者中的最大值  
  49.   
  50.     //如果左孩子都是连续区间,那么父节点的左连续区间除了左孩子的左连续区间,还要加上右孩子的左连续区间  
  51.     if(st[i<<1].ls==st[i<<1].r-st[i<<1].l+1)  
  52.     {  
  53.         st[i].ls+=st[(i<<1)|1].ls;  
  54.     }  
  55.     //如果右孩子都是连续区间,那么父节点的右连续区间除了右孩子的右连续区间,还要加上左孩子的右连续区间  
  56.     if(st[(i<<1)|1].rs==st[(i<<1)|1].r-st[(i<<1)|1].l+1)  
  57.     {  
  58.         st[i].rs+=st[i<<1].rs;  
  59.     }  
  60. }  
  61.   
  62. int query(int i,int p)  
  63. {  
  64.     //该区间没有1或者找到该位置或者该区间全是1  
  65.     if(st[i].ms==0||st[i].l==st[i].r||st[i].ms==st[i].r-st[i].l+1)  
  66.         return st[i].ms;  
  67.     int mid=(st[i].l+st[i].r)>>1;  
  68.   
  69.     if(p<=mid)//向左孩子中查询  
  70.     {  
  71.         if(p>=st[i<<1].r-st[i<<1].rs+1) //st[i<<1].r-st[i<<1].rs+1为左孩子节点右连续区间的左边界,比如[6,8]的长度为3,那么左边界就为8-3+1=6  
  72.             return query(i<<1,p)+query((i<<1)|1,mid+1);  
  73.         else  
  74.             return query(i<<1,p);  
  75.         //如果位置p在左孩子节点的右连续区间中,那么还要加上右孩子节点的左连续区间  
  76.         //比如左孩子[1,4],其右连续区间为[2,4],右孩子节点为[5,7],右孩子左连续区间为[5,6],位置p为3,那么包括3的连续区间不仅在[2,4]中,  
  77.         //还在[5,6]中,即包括3的最大连续区间为[2,6]  
  78.     }  
  79.     else//在右孩子中查询  
  80.     {  
  81.         if(p<=st[(i<<1)|1].l+st[(i<<1)|1].ls-1)  
  82.             return query((i<<1)|1,p)+query(i<<1,mid);  
  83.         else  
  84.             return query((i<<1)|1,p);  
  85.         //如果位置p在右孩子节点的左连续区间中,还要加上左孩子节点的右连续区间  
  86.     }  
  87. }  
  88.   
  89. int n,m,p;  
  90. char cm[2];  
  91. stack<int>sta;  
  92.   
  93. int main()  
  94. {  
  95.     while(rd2(n,m)!=EOF)  
  96.     {  
  97.         while(!sta.empty())  
  98.             sta.pop();  
  99.         build(1,1,n);  
  100.         while(m--)  
  101.         {  
  102.             scanf("%s",cm);  
  103.             if(cm[0]=='D')  
  104.             {  
  105.                 rd(p);  
  106.                 update(1,p,'D');  
  107.                 sta.push(p);  
  108.             }  
  109.             else if(cm[0]=='R')  
  110.             {  
  111.                 p=sta.top();  
  112.                 sta.pop();  
  113.                 update(1,p,'R');  
  114.             }  
  115.             else  
  116.             {  
  117.                 rd(p);  
  118.                 printf("%d\n",query(1,p));  
  119.             }  
  120.         }  
  121.     }  
  122.     return 0;  
  123. }  

hdu 3974 Assign the task

http://acm.hdu.edu.cn/showproblem.php?pid=3974

有n个员工,编号1-n,存在一些上司下属关系, a是c的上司,c是b的上司,那么a也是b的上司,没有上司的员工成为领导。给出n-1条上司下属关系,那么这n个员工关系就组成了一棵树,当给编号为i的员工分配任务时,该员工和其所有的下属都接受刚分配的任务,放弃前一个任务(如果有的话),查询编号为i的员工当前正做着哪一个任务。任务由数字表示。

在树中,父亲节点以及其所有的子节点是同时更新的,我们首先要把这些节点映射到区间上去,用dfs给这些点重新编号,start[i]表示该节点的新编号,end[i]表示其所有子节点的最后一个编号,那么执行更新操作时,只要对区间[start[i], end [i] ] 进行更新就可以了,把该区间的任务替换为新任务。


只要把树映射到线段区间上,就好操作了,建立线段树对其操作,区间更新,查询单点值,lazy表示分配的任务。

[cpp] view plain copy
  1. #define rd(x) scanf("%d",&x)  
  2. #define rd2(x,y)  scanf("%d%d",&x,&y)  
  3. #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)  
  4. using namespace std;  
  5. typedef long long ll;  
  6. const int inf=0x3f3f3f3f;  
  7. const int maxn=50010;  
  8.   
  9. int number;//给节点编号  
  10. int start[maxn],end[maxn];//第i个节点的编号,以及其孩子节点的最后一个编号,要更新,就更新这一段区间,对一个点操作,也就是对一段区间操作  
  11. vector<int>vc[maxn];//邻接表存边  
  12.   
  13. void init()  
  14. {  
  15.     number=0;  
  16.     for(int i=0;i<maxn;i++)  
  17.         vc[i].clear();  
  18. }  
  19.   
  20. void dfs(int u)//编号  
  21. {  
  22.     ++number;  
  23.     start[u]=number;  
  24.     int len=vc[u].size();  
  25.     for(int i=0;i<len;i++)  
  26.     {  
  27.         dfs(vc[u][i]);  
  28.     }  
  29.     end[u]=number;  
  30. }  
  31.   
  32. struct ST  
  33. {  
  34.     int l,r;  
  35.     int lazy;//分配的是什么任务  
  36. }st[maxn<<2];  
  37.   
  38. void pushDown(int i)  
  39. {  
  40.     if(st[i].lazy!=-1)  
  41.     {  
  42.         st[i<<1].lazy=st[(i<<1)|1].lazy=st[i].lazy;  
  43.         st[i].lazy=-1;  
  44.     }  
  45. }  
  46.   
  47. void build(int i,int l,int r)  
  48. {  
  49.     st[i].l=l;st[i].r=r;  
  50.     st[i].lazy=-1;  
  51.     if(st[i].l==st[i].r)  
  52.         return;  
  53.     int mid=(st[i].l+st[i].r)>>1;  
  54.     build(i<<1,l,mid);  
  55.     build((i<<1)|1,mid+1,r);  
  56. }  
  57.   
  58. void update(int i,int l,int r,int val)  
  59. {  
  60.     if(st[i].l==l&&st[i].r==r)  
  61.     {  
  62.         st[i].lazy=val;  
  63.         return;  
  64.     }  
  65.     pushDown(i);  
  66.     int mid=(st[i].l+st[i].r)>>1;  
  67.     if(r<=mid)  
  68.         update(i<<1,l,r,val);  
  69.     else if(l>mid)  
  70.         update((i<<1)|1,l,r,val);  
  71.     else  
  72.     {  
  73.         update(i<<1,l,mid,val);  
  74.         update((i<<1)|1,mid+1,r,val);  
  75.     }  
  76. }  
  77.   
  78. int query(int i,int p)  
  79. {  
  80.     if(st[i].l==st[i].r)  
  81.         return st[i].lazy;  
  82.     pushDown(i);  
  83.     int mid=(st[i].l+st[i].r)>>1;  
  84.     if(p<=mid)  
  85.         return query(i<<1,p);  
  86.     else  
  87.         return query((i<<1)|1,p);  
  88. }  
  89. int n,m;  
  90. char cm[2];  
  91. int cas=1;  
  92. int u,v;  
  93. bool vis[maxn];  
  94.   
  95. int main()  
  96. {  
  97.     int t;  
  98.     rd(t);  
  99.     while(t--)  
  100.     {  
  101.         printf("Case #%d:\n",cas++);  
  102.         rd(n);  
  103.         init();  
  104.         memset(vis,0,sizeof(vis));  
  105.         for(int i=1;i<n;i++)  
  106.         {  
  107.             rd2(u,v);  
  108.             vc[v].push_back(u);  
  109.             vis[u]=1;  
  110.         }  
  111.         for(int i=1;i<=n;i++)  
  112.         {  
  113.             if(!vis[i])//找到根节点,只有根节点没有被访问过  
  114.             {  
  115.                 dfs(i);  
  116.                 break;  
  117.             }  
  118.         }  
  119.         build(1,1,number);  
  120.         rd(m);  
  121.         while(m--)  
  122.         {  
  123.             scanf("%s",cm);  
  124.             if(cm[0]=='C')  
  125.             {  
  126.                 rd(u);  
  127.                 printf("%d\n",query(1,start[u]));//start[u]是该节点在线段中的编号  
  128.             }  
  129.             else  
  130.             {  
  131.                 rd2(u,v);  
  132.                 update(1,start[u],end[u],v);  
  133.             }  
  134.         }  
  135.     }  
  136.     return 0;  
  137. }  

sdut 2880 Devour Magic

http://www.sdutacm.org/sdutoj/problem.php?action=showproblem&problemid=2880

线段树操作:区间更新,总区间同时增加一个数,查询指定区间的和,查询后将该区间清0.

用到了两个lazy,一个是增量,一个是是否清0,Pushdown的时候,清0的lazy优先。

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <stdio.h>  
  3. #include <algorithm>  
  4. #include <string.h>  
  5. #include <stdlib.h>  
  6. #include <cmath>  
  7. #include <iomanip>  
  8. #include <vector>  
  9. #include <set>  
  10. #include <map>  
  11. #include <stack>  
  12. #include <queue>  
  13. #include <cctype>  
  14. using namespace std;  
  15. typedef long long ll;  
  16. const int maxn=100010;  
  17.   
  18. struct ST  
  19. {  
  20.     int l,r;  
  21.     ll sum;  
  22.     ll lazy;//懒惰标记  
  23.     ll lazy0;//该区间是否清0了  
  24. }st[maxn<<2];  
  25.   
  26. void pushUp(int i)  
  27. {  
  28.     st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;  
  29. }  
  30.   
  31. void pushDown(int i,int len)  
  32. {  
  33.     if(st[i].lazy0!=0)  
  34.     {  
  35.         st[i<<1].lazy0=st[(i<<1)|1].lazy0=st[i].lazy0;  
  36.         st[i<<1].sum=0;  
  37.         st[(i<<1)|1].sum=0;  
  38.         st[i<<1].lazy=0;  
  39.         st[(i<<1)|1].lazy=0;  
  40.         st[i].lazy0=0;  
  41.     }  
  42.     if(st[i].lazy!=0)  
  43.     {  
  44.         st[i<<1].lazy+=st[i].lazy;  
  45.         st[(i<<1)|1].lazy+=st[i].lazy;  
  46.         st[i<<1].sum+=ll(len-(len>>1))*st[i].lazy;  
  47.         st[(i<<1)|1].sum+=ll(len>>1)*st[i].lazy;  
  48.         st[i].lazy=0;  
  49.     }  
  50. }  
  51.   
  52. void build(int i,int l,int r)  
  53. {  
  54.     st[i].l=l;st[i].r=r;  
  55.     st[i].lazy=st[i].lazy0=0;  
  56.     st[i].sum=0;  
  57.     if(st[i].l==st[i].r)  
  58.         return;  
  59.     int mid=(st[i].l+st[i].r)>>1;  
  60.     build(i<<1,l,mid);  
  61.     build((i<<1)|1,mid+1,r);  
  62. }  
  63.   
  64. void update(int i,int l,int r,int val)  
  65. {  
  66.     if(val!=0)  
  67.     {  
  68.         if(st[i].l==l&&st[i].r==r)  
  69.         {  
  70.             st[i].lazy+=val;  
  71.             st[i].sum+=ll(r-l+1)*val;  
  72.             return;  
  73.         }  
  74.     }  
  75.     else  
  76.     {  
  77.         if(st[i].l==l&&st[i].r==r)  
  78.         {  
  79.             st[i].sum=0;  
  80.             st[i].lazy0=1;  
  81.             st[i].lazy=0;//保证当前节点的维护的值正确,别忘了这一句  
  82.             return;  
  83.         }  
  84.         pushDown(i,st[i].r-st[i].l+1);  
  85.         int mid=(st[i].l+st[i].r)>>1;  
  86.         if(r<=mid)  
  87.             update(i<<1,l,r,val);  
  88.         else if(l>mid)  
  89.             update((i<<1)|1,l,r,val);  
  90.         else  
  91.         {  
  92.             update(i<<1,l,mid,val);  
  93.             update((i<<1)|1,mid+1,r,val);  
  94.         }  
  95.         pushUp(i);  
  96.     }  
  97. }  
  98.   
  99. ll query(int i,int l,int r)  
  100. {  
  101.     if(st[i].l==l&&st[i].r==r)  
  102.     {  
  103.         return st[i].sum;  
  104.     }  
  105.     int mid=(st[i].l+st[i].r)>>1;  
  106.     pushDown(i,st[i].r-st[i].l+1);  
  107.     if(r<=mid)  
  108.         return query(i<<1,l,r);  
  109.     else if(l>mid)  
  110.         return query((i<<1)|1,l,r);  
  111.     else  
  112.         return query(i<<1,l,mid)+query((i<<1)|1,mid+1,r);  
  113. }  
  114. int t[maxn];  
  115. int n,q;  
  116. ll ans;  
  117.   
  118. int main()  
  119. {  
  120.     int cas;  
  121.     t[0]=0;  
  122.     scanf("%d",&cas);  
  123.     while(cas--)  
  124.     {  
  125.         ans=0;  
  126.         scanf("%d%d",&n,&q);  
  127.         build(1,1,n);  
  128.         for(int i=1;i<=q;i++)  
  129.         {  
  130.             int l,r;  
  131.             scanf("%d%d%d",&t[i],&l,&r);  
  132.             update(1,1,n,t[i]-t[i-1]);  
  133.             ans+=query(1,l,r);  
  134.             update(1,l,r,0);  
  135.         }  
  136.         //printf("%I64d\n",ans);  
  137.         cout<<ans<<endl;  
  138.     }  
  139.     return 0;  
  140. }  
原创粉丝点击