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。

转载于:https://www.cnblogs.com/zig-zag/archive/2013/04/30/3051578.html

技术选型 【后端】:Java 【框架】:springboot 【前端】:vue 【JDK版本】:JDK1.8 【服务器】:tomcat7+ 【据库】:mysql 5.7+ 项目包含前后台完整源码。 项目都经过严格调试,确保可以运行! 具体项目介绍可查看博主文章或私聊获取 助力学习实践,提升编程技能,快来获取这份宝贵的资源吧! 在当今快速发展的信息技术领域,技术选型是决定一个项目成功与否的重要因素之一。基于以下的技术栈,我们为您带来了一份完善且经过实践验证的项目资源,让您在学习和提升编程技能的道路上事半功倍。以下是该项目的技术选型和其组件的详细介绍。 在后端技术方面,我们选择了Java作为编程语言。Java以其稳健性、跨平台性和丰富的库支持,在企业级应用中处于领导地位。项目采用了流行的Spring Boot框架,这个框架以简化Java企业级开发而闻名。Spring Boot提供了简洁的配置方式、内置的嵌入式服务器支持以及强大的生态系统,使开发者能够更高效地构建和部署应用。 前端技术方面,我们使用了Vue.js,这是一个用于构建用户界面的渐进式JavaScript框架。Vue以其易上手、灵活和性能出色而受到开发者的青睐,它的组件化开发思想也有助于提高代码的复用性和可维护性。 项目的编译和运行环境选择了JDK 1.8。尽管Java已经推出了更新的版本,但JDK 1.8依旧是一种成熟且稳定的选择,广泛应用于各类项目中,确保了兼容性和稳定性。 在服务器方面,本项目部署在Tomcat 7+之上。Tomcat是Apache软件基金会下的一个开源Servlet容器,也是应用最为广泛的Java Web服务器之一。其稳定性和可靠的性能表现为Java Web应用提供了坚实的支持。 据库方面,我们采用了MySQL 5.7+。MySQL是一种高效、可靠且使用广泛的关系型据库管理系统,5.7版本在性能和功能上都有显著的提升。 值得一提的是,该项目包含了前后台的完整源码,并经过严格调试,确保可以顺利运行。通过项目的学习和实践,您将能更好地掌握从后端到前端的完整开发流程,提升自己的编程技能。欢迎参考博主的详细文章或私信获取更多信息,利用这一宝贵资源来推进您的技术成长之路!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值