线段树之区间合并

1.poj3667 Hotelhttp://poj.org/problem?id=3667

题意:有N个房间,M次操作。有两种操作

(1)"1a",表示找到连续的长度为a的空房间,如果有多解,优先左边的,即表示入住。

(2)"2 b len",把起点为b长度的len的房间清空,即退房

#include <iostream>
#include <algorithm>
#include <cstdio>
#define N 50005
using namespace std;
typedef struct node{
    int l;int r;int l1;int r1;int len1;int len2;int len;int id;
    int flag;
}node;
node d[N<<2];
void push(int root){
    if(d[root].flag==1){
       d[root<<1].l1=1;d[root<<1].r1=1;d[root<<1].len1=0;d[root<<1].len2=0;d[root<<1].len=0;d[root<<1].id=d[root<<1].l;
       d[root<<1|1].l1=1;d[root<<1|1].r1=1;d[root<<1|1].len1=0;d[root<<1|1].len2=0;d[root<<1|1].len=0;d[root<<1|1].id=d[root<<1|1].l;
       d[root<<1].flag=1;d[root<<1|1].flag=1;d[root].flag=-1;
    }
    else if(d[root].flag==0){
        int t=d[root<<1].r-d[root<<1].l+1;
        d[root<<1].l1=0;d[root<<1].r1=0;d[root<<1].len1=t;d[root<<1].len2=t;d[root<<1].len=t;d[root<<1].id=d[root<<1].l;
        t=d[root<<1|1].r-d[root<<1|1].l+1;
        d[root<<1|1].l1=0;d[root<<1|1].r1=0;d[root<<1|1].len1=t;d[root<<1|1].len2=t;d[root<<1|1].len=t;d[root<<1|1].id=d[root<<1|1].l;
        d[root<<1].flag=0;d[root<<1|1].flag=0;d[root].flag=-1;
    }
}
void up(int root){
    d[root].l1=d[root<<1].l1;d[root].r1=d[root<<1|1].r1;
    if(d[root<<1].len>=d[root<<1|1].len){
        d[root].len=d[root<<1].len;d[root].id=d[root<<1].id;
    }
    else {
        d[root].len=d[root<<1|1].len;d[root].id=d[root<<1|1].id;
    }
    if(d[root<<1].r1==0&&d[root<<1].r1==d[root<<1|1].l1){
        if(d[root<<1].len2+d[root<<1|1].len1>=d[root].len){
            d[root].len=d[root<<1].len2+d[root<<1|1].len1;
            if(d[root<<1].len2+d[root<<1|1].len1==d[root].len) d[root].id=min(d[root].id,d[root<<1].r-d[root<<1].len2+1);
            else d[root].id=d[root<<1].r-d[root<<1].len2+1;
        }
        if(d[root<<1].len2==d[root<<1].r-d[root<<1].l+1) d[root].len1=d[root<<1].len1+d[root<<1|1].len1;
        else d[root].len1=d[root<<1].len1;
        if(d[root<<1|1].len1==d[root<<1|1].r-d[root<<1|1].l+1) d[root].len2=d[root<<1|1].len2+d[root<<1].len2;
        else d[root].len2=d[root<<1|1].len2;
    }
    else{
        d[root].len1=d[root<<1].len1;d[root].len2=d[root<<1|1].len2;
    }
}
void built(int root,int l,int r){
    if(l==r){
        d[root].l=r;d[root].r=r;d[root].l1=0;d[root].r1=0;d[root].len1=1;d[root].len2=1;d[root].len=1;d[root].id=l;
        d[root].flag=-1;
        return ;
    }
    int mid=(l+r)>>1;
    built(root<<1,l,mid);
    built(root<<1|1,mid+1,r);
    d[root].l=d[root<<1].l;d[root].r=d[root<<1|1].r;d[root].flag=-1;
    up(root);
}
void update(int root,int l,int r,int t){
    if(l<=d[root].l&&d[root].r<=r){
        d[root].flag=t;
        if(t==1){
            d[root].l1=1;d[root].r1=1;d[root].len1=0;d[root].len2=0;d[root].len=0;d[root].id=d[root].l;
        }
        else{
            d[root].l1=0;d[root].r1=0;d[root].len1=d[root].r-d[root].l+1;d[root].len2=d[root].r-d[root].l+1;d[root].len=d[root].r-d[root].l+1;
            d[root].id=d[root].l;
        }
        return ;
    }
    push(root);
    int mid=(d[root].l+d[root].r)>>1;
    if(l<=mid) update(root<<1,l,r,t);
    if(r>mid) update(root<<1|1,l,r,t);
    up(root);
}
int ans_id;
void query(int root,int t){
    if(d[root].r-d[root].l+1<t) return ;
    if(d[root].len<t) return ;
    if(d[root].r==d[root].l) {
        ans_id=d[root].l;return ;
    }
    push(root);
    if(d[root<<1].len>=t){
         ans_id=d[root].id;
        query(root<<1,t);
    }
    else{
     //  cout<<d[root<<1].len2<<" "<<d[root<<1|1].len1<<"  "<<ans_id<<"------------"<<endl;
        if(d[root<<1].r1==0&&d[root<<1].r1==d[root<<1|1].l1&&d[root<<1].len2+d[root<<1|1].len1>=t) ans_id=d[root<<1].r-d[root<<1].len2+1;
        else{
         //   cout<<d[root].l<<"===="<<d[root].r<<endl;
            query(root<<1|1,t);
        }
    }
    up(root);
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)==2){
          built(1,1,n);
          int t1,t2,t3;
          for(int i=1;i<=m;i++){
            scanf("%d",&t1);
            if(t1==1){
                scanf("%d",&t2);
                ans_id=N;
                query(1,t2);
            if(ans_id==N) printf("0\n");
            else {printf("%d\n",ans_id);
               update(1,ans_id,ans_id+t2-1,1);
            }
            }
            else{
             scanf("%d%d",&t2,&t3);
             update(1,t2,t2+t3-1,0);
            }
          }
    }
    return 0;
}

2.hdu3308 LCIS:http://acm.hdu.edu.cn/showproblem.php?pid=3308

给你N个整数,有两种操作,

(1)"U A B",表示把第A个数变成B,

(2)"QAB",表示查询区间[A,B]的最长连续上升序列。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<cstdio>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define N 100005
using namespace std;
typedef struct node{
    int first;int end;int l;int lc;int r;int rc;int mmax;
}node;
node a[N*4];
int b[N];
node pushchuan(node aa,node bb){
    int ans;
    node p;
    p.first=aa.first;p.end=bb.end;
    if(aa.r<bb.l){
        ans=aa.rc+bb.lc;
        p.mmax=max(aa.mmax,max(ans,bb.mmax));
        p.l=aa.l;
        if(aa.end-aa.rc+1==aa.first)  p.lc=aa.end-aa.first+1+bb.lc;
        else p.lc=aa.lc;
        p.r=bb.r;
        if(bb.end-bb.rc+1==bb.first)  p.rc=bb.end-bb.first+1+aa.rc;
        else p.rc=bb.rc;
    }
    else{
        p.l=aa.l;p.lc=aa.lc;p.r=bb.r;p.rc=bb.rc;
        p.mmax=max(aa.mmax,bb.mmax);
    }
    return p;
}
void built(int root,int first,int end){
    if(first==end){
        a[root].first=first;a[root].end=end;a[root].l=b[first];a[root].lc=1;a[root].r=b[first];a[root].rc=1;
        a[root].mmax=1;
        return ;
    }
    int mid=(first+end)/2;
    built(root*2,first,mid);
    built(root*2+1,mid+1,end); 
    a[root]=pushchuan(a[root*2],a[root*2+1]);
//    a[root].first=a[root*2].first;a[root].end=a[root*2+1].end;
}
int maxx;
node tt;
void Q(int root,int first,int end,int l,int r){
    if(l<=first&&end<=r){
        if(l==first)  tt=a[root];
        else tt=pushchuan(tt,a[root]);
        return ;
    }
    int mid=(first+end)/2;
    if(l<=mid)  Q(root*2,first,mid,l,r);
    if(r>mid)  Q(root*2+1,mid+1,end,l,r);
}
void U(int root,int first,int end,int e,int w){
    if(first==end){
        a[root].l=w;a[root].r=w;
    //    cout<<"====="<<first<<endl;
        return ;
    }
    int mid=(first+end)/2;
    if(e<=mid)  U(root*2,first,mid,e,w);
    else U(root*2+1,mid+1,end,e,w);
    a[root]=pushchuan(a[root*2],a[root*2+1]);
//    cout<<a[root].first<<" "<<a[root].end<<" "<<a[root].mmax<<"  "<<a[root].l<<"  "<<a[root].lc<<"========"<<a[root].r<<"   "<<a[root].rc<<endl;
}
int main(){
    int T;
    scanf("%d",&T);
    int n,m;
    char ch;
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)   scanf("%d",&b[i]);
        built(1,1,n);
        int aa,bb;
        for(int i=1;i<=m;i++){
            scanf(" %c",&ch);
            if(ch=='U'){
                scanf("%d%d",&aa,&bb);aa++;
                U(1,1,n,aa,bb);
            }
            else if(ch=='Q'){
                scanf("%d%d",&aa,&bb);
                aa++;bb++;
                Q(1,1,n,aa,bb);
                printf("%d\n",tt.mmax);
            }
        }
    }
    return 0;
}

3.hdu3397 Sequence operation:http://acm.hdu.edu.cn/showproblem.php?pid=3397

有N个为0或为1的数,有M个操作,操作有5种类型。

(1)“0 a b”,表示将区间[a,b]范围内的数全部置0。

(2)“1 a b”,表示将区间[a,b]内的数全部置1。

(3)"2 a b",表示将区间[a,b]内的数0变成1,1变成0。

(4)"3ab",表示查询[a,b]范围内1的数。

(5)"4 a b",表示查询[a,b]范围内最长的连续的1。

题解:情况很多 分开考虑维护即可

#include <bits/stdc++.h>
#define N 100005
using namespace std;
typedef struct node{
    int flag1;int flag2;int l1;int r1;int len1;int len2;int len;
    int sum;int len11;int len22;int lenn;
}node;
node d[N<<2];
int a[N];
void swap1(int &a,int &b){
    int t=a;a=b;b=t;
}
void up(int root,int t1,int t2){
    int mid=(t1+t2)>>1;
    d[root].sum=d[root<<1].sum+d[root<<1|1].sum;
    d[root].len=max(d[root<<1].len,d[root<<1|1].len);
    d[root].l1=d[root<<1].l1;d[root].r1=d[root<<1|1].r1;
    if(d[root<<1].r1==1&&d[root<<1].r1==d[root<<1|1].l1){
        d[root].len=max(d[root].len,d[root<<1].len2+d[root<<1|1].len1);
        if(d[root<<1].len1==mid-t1+1) d[root].len1=d[root<<1].len1+d[root<<1|1].len1;
        else d[root].len1=d[root<<1].len1;
        if(d[root<<1|1].len2==t2-mid) d[root].len2=d[root<<1|1].len2+d[root<<1].len2;
        else d[root].len2=d[root<<1|1].len2;
    }
    else{
        d[root].len1=d[root<<1].len1;d[root].len2=d[root<<1|1].len2;
    }
    d[root].lenn=max(d[root<<1].lenn,d[root<<1|1].lenn);
    if(d[root<<1].r1==0&&d[root<<1].r1==d[root<<1|1].l1){
        d[root].lenn=max(d[root].lenn,d[root<<1].len22+d[root<<1|1].len11);
        if(d[root<<1].len11==mid-t1+1) d[root].len11=d[root<<1].len11+d[root<<1|1].len11;
        else d[root].len11=d[root<<1].len11;
        if(d[root<<1|1].len22==t2-mid) d[root].len22=d[root<<1|1].len22+d[root<<1].len22;
        else d[root].len22=d[root<<1|1].len22;
    }
    else{
        d[root].len11=d[root<<1].len11;d[root].len22=d[root<<1|1].len22;
    }
}
void push(int root,int t1,int t2){
    int mid=(t1+t2)>>1;
    if(d[root].flag1!=-1){
        d[root<<1].flag2=0;d[root<<1|1].flag2=0;d[root<<1].flag1=d[root].flag1;d[root<<1|1].flag1=d[root].flag1;
        if(d[root].flag1==0){
            d[root<<1].l1=0;d[root<<1].r1=0;d[root<<1].len1=0;d[root<<1].len2=0;d[root<<1].len=0;d[root<<1].sum=0;
            d[root<<1].len11=mid-t1+1;d[root<<1].len22=mid-t1+1;d[root<<1].lenn=mid-t1+1;
            d[root<<1|1].l1=0;d[root<<1|1].r1=0;d[root<<1|1].len1=0;d[root<<1|1].len2=0;d[root<<1|1].len=0;d[root<<1|1].sum=0;
            d[root<<1|1].len11=t2-mid;d[root<<1|1].len22=t2-mid;d[root<<1|1].lenn=t2-mid;
        }
        else{
            d[root<<1].l1=1;d[root<<1].r1=1;d[root<<1].len1=(mid-t1+1);d[root<<1].len2=(mid-t1+1);
            d[root<<1].len=mid-t1+1;d[root<<1].sum=mid-t1+1;
            d[root<<1].len11=0;d[root<<1].len22=0;d[root<<1].lenn=0;
            int t=t2-mid;
            d[root<<1|1].l1=1;d[root<<1|1].r1=1;d[root<<1|1].len1=t;d[root<<1|1].len2=t;d[root<<1|1].len=t;d[root<<1|1].sum=t;
            d[root<<1|1].len11=0;d[root<<1|1].len22=0;d[root<<1|1].lenn=0;
        }
        d[root].flag1=-1;
        if(d[root].flag2%2==1){
         d[root<<1].flag2=1;d[root<<1|1].flag2=1;
         d[root<<1].sum=(mid-t1+1-d[root<<1].sum);d[root<<1|1].sum=(t2-mid-d[root<<1|1].sum);
         swap(d[root<<1].len,d[root<<1].lenn);swap(d[root<<1].len1,d[root<<1].len11);swap(d[root<<1].len2,d[root<<1].len22);
         swap(d[root<<1|1].len,d[root<<1|1].lenn);swap(d[root<<1|1].len1,d[root<<1|1].len11);swap(d[root<<1|1].len2,d[root<<1|1].len22);
         d[root<<1].l1=(d[root<<1].l1==0)?1:0;d[root<<1].r1=(d[root<<1].r1==0)?1:0;
         d[root<<1|1].l1=(d[root<<1|1].l1==0)?1:0;d[root<<1|1].r1=(d[root<<1|1].r1==0)?1:0;
        }
        d[root].flag2=0;
    }
    else{
        if(d[root].flag2%2==1){
         d[root<<1].flag2=(d[root<<1].flag2+1)%2;d[root<<1|1].flag2=(d[root<<1|1].flag2+1)%2;
         d[root<<1].sum=(mid-t1+1-d[root<<1].sum);d[root<<1|1].sum=(t2-mid-d[root<<1|1].sum);
         swap(d[root<<1].len,d[root<<1].lenn);swap(d[root<<1].len1,d[root<<1].len11);swap(d[root<<1].len2,d[root<<1].len22);
         swap(d[root<<1|1].len,d[root<<1|1].lenn);swap(d[root<<1|1].len1,d[root<<1|1].len11);swap(d[root<<1|1].len2,d[root<<1|1].len22);
         d[root<<1].l1=(d[root<<1].l1==0)?1:0;d[root<<1].r1=(d[root<<1].r1==0)?1:0;
         d[root<<1|1].l1=(d[root<<1|1].l1==0)?1:0;d[root<<1|1].r1=(d[root<<1|1].r1==0)?1:0;
        }
        d[root].flag2=0;
    }
}
void built(int root,int l,int r){
    if(l==r){
        d[root].flag1=-1;d[root].flag2=0;d[root].l1=a[l];d[root].r1=a[l];d[root].len1=a[l];d[root].len2=a[l];
        d[root].len=a[l];d[root].sum=a[l];
        d[root].len11=(d[root].len1==1)?0:1;d[root].len22=d[root].len11;d[root].lenn=d[root].len11;
        return ;
    }
    int mid=(l+r)>>1;
    built(root<<1,l,mid);
    built(root<<1|1,mid+1,r);
    d[root].flag1=-1;d[root].flag2=0;
    up(root,l,r);
}
void update1(int root,int l,int r,int l1,int r1,int t){
    if(l1<=l&&r<=r1){
        d[root].flag1=t;d[root].flag2=0;
        if(t==0){
            d[root].l1=0;d[root].r1=0;d[root].len1=0;d[root].len2=0;d[root].len=0;d[root].sum=0;
            d[root].len11=r-l+1;d[root].len22=r-l+1;d[root].lenn=r-l+1;
        }
        else{
            d[root].l1=1;d[root].r1=1;d[root].len1=(r-l+1);d[root].len2=(r-l+1);
            d[root].len=r-l+1;d[root].sum=r-l+1;
            d[root].len11=0;d[root].len22=0;d[root].lenn=0;
        }
       // cout<<l<<" "<<r<<"====="<<d[root].sum<<endl;
        return ;
    }
    push(root,l,r);
    int mid=(l+r)>>1;
    if(l1<=mid) update1(root<<1,l,mid,l1,r1,t);
    if(r1>mid) update1(root<<1|1,mid+1,r,l1,r1,t);
    //cout<<l<<" "<<r<<endl;
    up(root,l,r);

}
void update2(int root,int l,int r,int l1,int r1){
    if(l1<=l&&r<=r1){
        d[root].flag2++;
        d[root].sum=(r-l+1-d[root].sum);
        swap(d[root].len,d[root].lenn);swap(d[root].len1,d[root].len11);swap(d[root].len2,d[root].len22);
        d[root].l1=(d[root].l1==0)?1:0;d[root].r1=(d[root].r1==0)?1:0;
        return ;
    }
    push(root,l,r);
    int mid=(l+r)>>1;
    if(l1<=mid) update2(root<<1,l,mid,l1,r1);
    if(mid<r1) update2(root<<1|1,mid+1,r,l1,r1);
    up(root,l,r);
    //cout<<l<<"====="<<r<<" "<<d[root].len<<endl;
}
int ans;
void querty1(int root,int l,int r,int l1,int r1){
    if(l1<=l&&r<=r1){
    //    cout<<l<<"====="<<r<<" "<<d[root].sum<<endl;
        ans+=d[root].sum;
        return ;
    }
    push(root,l,r);
    int mid=(l+r)>>1;
    if(l1<=mid) querty1(root<<1,l,mid,l1,r1);
    if(r1>mid) querty1(root<<1|1,mid+1,r,l1,r1);
    up(root,l,r);
    //cout<<l<<" "<<r<<"======"<<d[root].sum<<endl;
}
bool tem;int ans1;node pp;bool tep;
void querty2(int root,int l,int r,int l1,int r1){
    if(l1<=l&&r<=r1){
           // cout<<l<<"===="<<r<<"  "<<d[root].len<<endl;
        if(tem==0){
            pp=d[root];ans1=pp.len;tem=1;
            if(pp.len1==(r-l+1)) tep=1;
        }
        else{
          ans1=max(ans1,d[root].len);
          if(pp.r1==1&&d[root].l1==pp.r1){
                ans1=max(ans1,pp.len2+d[root].len1);
                if(tep==1) pp.len1=pp.len1+d[root].len1;
                if(tep==1&&d[root].len1==(r-l+1)) tep=1;
                else tep=0;
                if(d[root].len2==(r-l+1)) pp.len2=d[root].len2+pp.len2;
                else pp.len2=d[root].len2;
            }
         else pp.len2=d[root].len2;
         pp.r1=d[root].r1;
        }
      //   cout<<pp.len1<<" "<<pp.len2<<endl;
      //   cout<<l<<" "<<r<<" "<<"========"<<ans1<<endl;
        return ;
    }
    push(root,l,r);
    int mid=(l+r)>>1;
    if(l1<=mid) querty2(root<<1,l,mid,l1,r1);
    if(r1>mid) querty2(root<<1|1,mid+1,r,l1,r1);
    up(root,l,r);
}
int main(){
   int T;scanf("%d",&T);
   while(T--){
     int n,m;scanf("%d%d",&n,&m);
     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
     built(1,1,n);
     for(int i=1;i<=m;i++){
        int t1,t2,t3;scanf("%d",&t1);
        if(t1==0){
            scanf("%d%d",&t2,&t3);t2++;t3++;
            update1(1,1,n,t2,t3,0);
        }
        else if(t1==1){
            scanf("%d%d",&t2,&t3);t2++;t3++;
            update1(1,1,n,t2,t3,1);
        }
        else if(t1==2){
            scanf("%d%d",&t2,&t3);t2++;t3++;
            update2(1,1,n,t2,t3);
        }
        else if(t1==3){
            scanf("%d%d",&t2,&t3);t2++;t3++;
           // cout<<t2<<" "<<t3<<endl;
            ans=0;
            querty1(1,1,n,t2,t3);
            printf("%d\n",ans);
        }
        else if(t1==4){
            scanf("%d%d",&t2,&t3);t2++;t3++;tem=0;tep=0;
            querty2(1,1,n,t2,t3);
            printf("%d\n",ans1);
        }
     }
   }
   return 0;
}

hdu1540 Tunnel Warfare:http://acm.hdu.edu.cn/showproblem.php?pid=1540

有N个村子排成一条直线,每个村子都连接了它的左右两个村子(除了最左边和最右边的外),有3种操作,

(1)"D x",表示将第x个村子摧毁。

(2)"Qx",表示查询与第x个村子直接和间接相连的村子有多少个。

(3)"R",表示将最早摧毁的村子复原。解题报告Here。

题意:没啥好说的傻逼维护区间合并....XJB搞一搞 栈维护就行

#include <bits/stdc++.h>
#define N 50005
using namespace std;
typedef struct node{
    int first;int end;int l;int r;
}node;
node a[4*N];
int b[N];
bool vis[N];
node pushchuan(node aa,node bb){
    node p;
    p.first=aa.first;p.end=bb.end;
    if(aa.l==(aa.end-aa.first+1)){
        p.l=aa.l+bb.l;
    }
    else{
        p.l=aa.l;
    }
    if(bb.r==(bb.end-bb.first+1)){
        p.r=bb.r+aa.r;
    }
    else{
        p.r=bb.r;
    }
    return p;
}
void built(int root,int first,int end){
    if(first==end){
        a[root].first=first;a[root].end=end;a[root].l=1;a[root].r=1;
        return ;
    }
    int mid=(first+end)>>1;
    built(root*2,first,mid);
    built(root*2+1,mid+1,end);
    node p=pushchuan(a[root*2],a[root*2+1]);
    a[root]=p;
}
void update(int root,int first,int end,int e,int w){
    if(first==end){
        if(w==1){
            a[root].l=1;a[root].r=1;
        }
        else{
            a[root].l=0;a[root].r=0;
        }
        return ;
    }
    int mid=(first+end)>>1;
    if(e<=mid) update(root*2,first,mid,e,w);
    else update(root*2+1,mid+1,end,e,w);
    node p=pushchuan(a[root*2],a[root*2+1]);
    a[root]=p;
}
int sum;
void query(int root,int first,int end,int e){
    if(first==end){
        sum++;
        return ;
    }
    int mid=(first+end)>>1;
    if(e<=mid){
        if(a[root*2].r>=(a[root*2].end-e+1)) sum+=a[root*2+1].l;
        query(root*2,first,mid,e);
    }
    else{
        if(a[root*2+1].l>=(e-a[root*2+1].first+1)) sum+=a[root*2].r;
        query(root*2+1,mid+1,end,e);
    }
}
int main(){
    int n,m;
   while(scanf("%d%d",&n,&m)==2){
    built(1,1,n);
    char ch;int t1;
    stack<int>s;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;i++){
        scanf(" %c",&ch);
        if(ch=='D'){
            scanf("%d",&t1);
            update(1,1,n,t1,0);
            s.push(t1);
            vis[t1]=1;
        }
        else if(ch=='R'){
            t1=s.top();s.pop();
            vis[t1]=0;
            update(1,1,n,t1,1);
        }
        else {
            scanf("%d",&t1);
            if(vis[t1]==1)  cout<<0<<endl;
            else{
                sum=0;
                query(1,1,n,t1);
            printf("%d\n",sum);
            }
        }
    }
    while(!s.empty())  s.pop();
}
    return 0;
}

  

posted @ 2017-11-20 23:50  wang9897  阅读(310)  评论(0编辑  收藏  举报