[HUST1407 山东省队选拔赛2008 郁闷的小J]【离线算法】【二分】【树状数组】★★

【题目连接】http://acm.hust.edu.cn/thx/problem.php?id=1407 (中文题目)

【算法分析】

这是一个好题。

首先有一个通用解法,平衡树+线段树,不过几乎肯定超时。(在线算法)

然后我YY到一个很经典的东西。P[j]-P[i-1]就得到i..j的信息。

于是对于一个询问,Q i j w。其实我们只要知道w在前j-1个位置有多少个,和w在前i个位置有多少个。就能通过一个减法得到答案。

然后我们把我们需要的信息抽出来。只维护他们。

每个信息的结构如下。{int Num,pos,key;}。其中Num表示询问的w。pos表示前pos个位置。key表示前pos个位置,Num出现几次?

然后我们观察假如change了一个数a[i]=p -> a[i]=w,该变的是哪些信息?

第一类,删除旧信息。对于所有Num=p,且pos>=i的信息,我们将他的key–

第二类,增加新信息。对于所有Num=w,且pos>=i的信息,我们将他的key++

好了。这里面我们需要的东西就显而易见了。

把信息按Num为第一关键字,pos为第二关键字排序,然后可以很容易用树状数组 or 线段树维护起来。

当然,我们当前位置+1。后面Num>w的是没有必要减回来的。因为我们最后是通过减法运算嘛。会互相抵消的。

【其它】

Orz。再删除了一次冗余的东西以后。排到了第一。

1 48386(2) edward_mj 4560 KB 1432 MS C++ 2382 B 2010-07-29 15:53:57 2 47267 lshmouse 6144 KB 1568 MS C++ 2727 B 2010-07-26 21:30:03 3 48114 foreverlin 5480 KB 1804 MS C++ 4771 B 2010-07-29 01:32:38 4 48102 WhatIsGenius 8548 KB 3128 MS C++ 1965 B 2010-07-28 23:09:53

【CODE】

#include #include #include #include #include using namespace std;
const int N=105555;
int n,m,tot;
int a[N];
struct Qu_t{int op,x,y,z;}Query[N];
struct List_t{int Num,pos;}L[N*2];
int Tr[N*2];

void init(){
     tot=0;
     scanf("%d%d",&n,&m);
     int i;
     for (i=1;i<=n;i++)
       scanf("%d",&a[i]);
     char tmp[10];
     for (i=1;i<=m;i++){
         scanf("%s",tmp);
         if (tmp[0]==’Q’){
           Query[i].op=0;
           scanf("%d%d%d",&Query[i].x,&Query[i].y,&Query[i].z);
           L[++tot].Num=Query[i].z; L[tot].pos=Query[i].y;
           L[++tot].Num=Query[i].z; L[tot].pos=Query[i].x-1;
         }
         else{
           Query[i].op=1;
           scanf("%d%d",&Query[i].x,&Query[i].y);
         }
     }
}

inline bool cmp(List_t A,List_t B){
     if (A.Num!=B.Num) return A.Num     return A.pos}

inline void Add(int k,int x){
     for (;k<=tot;k+=k&-k) Tr[k]+=x;
}

inline int Get_Sum(int k){
    int res=0;
    for (;k>0;k-=k&-k) res+=Tr[k];
    return res;
}

inline int Find(int key,int pos){
    int l=1,r=tot,mid;
    while (l      mid=(l+r)>>1;
      if (L[mid].Num                                                          else r=mid;
    }
    return l;
}

void pre_do(){
     sort(L+1,L+tot+1,cmp);
     int tmp=tot,i,x;
     tot=0;
     for (i=1;i<=tmp;i++){
         tot+=(i==1 || L[i].Num!=L[i-1].Num || L[i].pos!=L[i-1].pos);
         L[tot]=L[i];
     }
     for (i=1;i<=tot;i++) Tr[i]=0;
     for (i=1;i<=n;i++){
         x=Find(a[i],i);
         if (L[x].Num==a[i] && L[x].pos>=i) Add(x,1);
     }
}

void solve(){
     int i,j,x,y;
     for (i=1;i<=m;i++)
       if (Query[i].op==0){
         x=Get_Sum( Find ( Query[i].z,Query[i].y ) );
         y=Get_Sum( Find ( Query[i].z,Query[i].x-1 ) );
         printf("%dn",x-y);
       }
       else{
         x=Find(a[Query[i].x],Query[i].x);
         if (L[x].Num==a[Query[i].x] && L[x].pos>=Query[i].x) Add(x,-1);
        
         a[Query[i].x]=Query[i].y;
        
         x=Find(a[Query[i].x],Query[i].x);
         if (L[x].Num==a[Query[i].x] && L[x].pos>=Query[i].x) Add(x,1);
       }
}

int main(){
    init();
    pre_do();
    solve();
}

加入对话

12条评论

留下评论

回复 龙腾·乱舞 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注