• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
AC_Artist.zig_zag
然而我依然在补题、
博客园    首页    新随笔    联系   管理    订阅  订阅

bzoj2120 数颜色

这篇题解拖了好久......原因是一直不知道该怎么叙述。

首先,类似采花那个题,统计答案的时候要找到每个位置之前和它颜色相同的第一个位置pre[i],那么只要这个位置的pre[i]不在查询的[L,R]区间里,就说明这个颜色是这个区间里第一次出现的颜色,答案+1。动态修改的话我们可以用set来实现。

然后传统的树套树做法是将序列建一棵线段树,再给线段树每个节点建一棵平衡树,代表[l,r]这段区间。那么每次查询[L,R]的时候在线段树上走一下,找到相应位置,查找比询问L小的有多少个就行了。

但是有一种更为简洁的做法——整体二分!

以前都是用整体二分去搞一些第k小数问题,但这个题其实和第k小数有很大的联系:找区间k小数等价于找到一个数a使得区间内比它大的数有k-1个(问题1);而此题等价于找到一个k使得小于等于数a的数恰好有k个(问题2)。这似乎是非常对偶的两个问题,那么怎么用整体二分呢?我们回想找第k小数的过程,我们二分了一个值M,然后去找小于等于M的数有多少个,根据这个来划分询问。也就是说,在求问题1的时候我们一定要求出问题2的答案,那么我们不就可以利用这个过程直接把问题2求出来了吗?那么我们在判断询问的归属的时候,直接利用问题2的答案来判定,也就是根据当前答案+贡献值和询问左端点的大小比较。这样最后在每个询问上累计的贡献就是最终这个询问的答案。

我们再考虑一下整体二分的意义,实际上我们二分的就是问题2里的那个a,也就是我们维护的pre,这相当于在一棵权值线段树里面寻找答案。对值域的整体二分就相当于离线建立的一棵隐式的权值线段树,但我们用权值线段树查询的时候是单路求值,用整体二分的话就是多路求值,是把所有的询问套在一起做,一起出答案 。

color
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<set>
  7 #define maxn 24000
  8 #define high 1000002
  9 using namespace std;
 10 struct query
 11 {
 12     int x,y,cur,tp,s;
 13 }q[maxn],q1[maxn],q2[maxn];
 14 int a[maxn],ans[maxn],tmp[maxn];
 15 int n,m,num,cnt;
 16 struct bit
 17 {
 18     int b[maxn];
 19     void add(int x,int z)
 20     {
 21         for (int i=x;i<=n;i+=(i&-i)) b[i]+=z;
 22     }
 23     int ask(int x)
 24     {
 25         int tmp=0;
 26         for (int i=x;i>0;i-=(i&-i)) tmp+=b[i];
 27         return tmp;
 28     }
 29 }t;
 30 set<int> s[high];
 31 set<int>::iterator p;
 32 //set是颜色库,用来存储同种颜色的位置
 33 
 34 void divide(int head,int tail,int l,int r)
 35 {
 36     if (head>tail) return;
 37     if (l==r)
 38     {
 39         for (int i=head;i<=tail;i++)
 40             if (q[i].tp==3) ans[q[i].s]=q[i].cur;
 41         return ;
 42     }
 43     int mid=(l+r)>>1;
 44     for (int i=head;i<=tail;i++)
 45     {
 46         if (q[i].tp==1&&q[i].y<=mid) t.add(q[i].x,1);
 47         else
 48         if (q[i].tp==2&&q[i].y<=mid) t.add(q[i].x,-1);
 49         else
 50         if (q[i].tp==3) tmp[i]=t.ask(q[i].y)-t.ask(q[i].x-1);
 51     }
 52     for (int i=head;i<=tail;i++) 
 53     {
 54         if (q[i].tp==1&&q[i].y<=mid) t.add(q[i].x,-1);
 55         else
 56         if (q[i].tp==2&&q[i].y<=mid) t.add(q[i].x,1);
 57     }
 58     int l1=0,l2=0;
 59     for (int i=head;i<=tail;i++)
 60     {
 61         if (q[i].tp==3)
 62             if (q[i].x<=mid) q1[++l1]=q[i];
 63             else 
 64             {
 65                 q[i].cur+=tmp[i];
 66                 q2[++l2]=q[i];
 67             }
 68         else
 69             if (q[i].y<=mid) q1[++l1]=q[i];
 70             else q2[++l2]=q[i];
 71     }
 72     for (int i=1;i<=l1;i++) q[head+i-1]=q1[i];
 73     for (int i=1;i<=l2;i++) q[head+l1+i-1]=q2[i];
 74     divide(head,head+l1-1,l,mid);
 75     divide(tail-l2+1,tail,mid+1,r);
 76 }
 77 
 78 int prev(int x,int c)
 79 {
 80     p=s[c].lower_bound(x);
 81     if (p!=s[c].begin()) return *(--p);
 82     else return 0;
 83 }
 84 int succ(int x,int c)
 85 {
 86     p=s[c].upper_bound(x);
 87     if (p!=s[c].end()) return *p;
 88     else return 0;
 89 }
 90 
 91 int main()
 92 {
 93     scanf("%d%d",&n,&m);
 94     for (int i=1;i<=n;i++) 
 95     {
 96         scanf("%d",&a[i]);
 97         s[a[i]].insert(i);
 98         int l=prev(i,a[i]);
 99         q[++num].tp=1;
100         q[num].x=i; q[num].y=l;
101     }
102     char sign;
103     int x,y;
104     for (int i=1;i<=m;i++)
105     {
106         scanf("\n%c%d%d",&sign,&x,&y);
107         if (sign=='Q') 
108         {
109             q[++num].tp=3;
110             q[num].x=x; q[num].y=y;
111             q[num].s=++cnt;
112         }
113         else
114         {
115             int l1=prev(x,a[x]),l2=prev(x,y);
116             int r1=succ(x,a[x]),r2=succ(x,y);
117             if (r1)
118             {
119                 q[++num].tp=2; q[num].x=r1; q[num].y=x;
120                 q[++num].tp=1; q[num].x=r1; q[num].y=l1;
121             }
122             q[++num].tp=2; q[num].x=x; q[num].y=l1;
123             q[++num].tp=1; q[num].x=x; q[num].y=l2;
124             if (r2)
125             {
126                 q[++num].tp=2; q[num].x=r2; q[num].y=l2;
127                 q[++num].tp=1; q[num].x=r2; q[num].y=x;
128             }
129             s[a[x]].erase(x);
130             a[x]=y;
131             s[a[x]].insert(x);
132         }
133     }
134     divide(1,num,0,high);
135     for (int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
136     return 0;
137 }
138     
139     
140     

 

感谢Martin大神提供思路orz。

AC without art, no better than WA !
posted @ 2013-04-30 09:06  Zig_zag  阅读(1149)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3