[离散化模板][洛谷1955]程序自动化分析
开篇
这是很久之前做过的题了,也是我第一次接触离散化的题。之前一直忘了记下来,今天补写一篇离散化博客。
题目
原链接:https://www.luogu.com.cn/problem/P1955
(懒得复制粘贴再改格式了,大家直接看吧)
解说
最开始看过一遍题这东西简单的一批啊,并查集板子吧。先处理相等关系,相等的扔到一个集子里,然后判断不等关系,每个不等关系中的两个数要是在一个集子里就不成立,一遍判断下来啥事没有就成立。又能水过一道题了哈哈哈!
开始一写,笑容渐渐凝固在脸上:等一下这个数的大小小于等于1e9,我的father数组开不下啊[托腮][托腮]。最开始还不会离散化,想的是开几个int数组,用要存的数%每个数组的大小,分开储存。比如开10个1e8的数组,100000005就存在第二个数组第五个,10就存在第一个数组第十个这样的。后来一想出题人肯定不会是想让我们这样的,极大的可能会卡个内存啥的,这种方法实际上的内存是没有改变的,所以估计会被卡掉,而且极其麻烦。
再仔细看一看,题目中n<=1e6,描述的每个关系涉及两个数,那么最多就涉及2000000个数,那么就相当于1e9的储存空间中有大部分是空着的,简直是在浪费空间,能优化吗?显然可以,在这个题中,我们需要的只是区分每一个数,而不需要每个数的具体大小,那么我们是不是就可以把一些大一点的数用小一点的数代替呢?就像暗号一样,反正最后只要保证每个数有专属于它的暗号,可以区分就可以了。这就是离散化。
那么具体该怎么做呢?毕竟之前没接触过离散化所以此时我在网上一顿搜索应该情有可原
那么,离散化一共包括三部。一、排序。二、去重。三、映射。下面一步一步讲解。
排序
这是非常简单的一步。一边读入一边把所有涉及到的数字存在all数组里,然后sort排序就行了(P.S. 可以顺便在这里特判一下一个不等关系都没有就直接YES然后continue)。
1 cin>>n;
2 memset(all,0,sizeof(all));
3 int judge=0;
4 for (int i=1;i<=n;i++){
5 cin>>cun[i].a>>cun[i].b>>cun[i].inf;
6 if(!cun[i].inf) judge=1;
7 all[tot]=cun[i].a; tot++;
8 all[tot]=cun[i].b; tot++;
9 }
10 if(!judge){cout<<"YES"<<endl;continue;}
11 sort(all+1,all+tot);
去重及映射
这就是之前不会的原因了,这部分涉及两个之前没接触过的函数lower_bound()和unique()(都属于algorithm库)。unique的作用是“去掉”容器中重复元素(不一定要求数组有序),它会把重复的元素添加到容器末尾(所以数组大小并没有改变),而返回值是去重之后的尾地址(因此我们可以相当于地认为数组大小变成了unique(all+1,all+tot)-all-1,后面的元素就都相当于被抹掉了,那么我们就得到了一个本题涉及的所有数的不重复列表,这样的话我们就可以把一个数的下标直接当成它的“暗号”),而函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置(注意它返回的也是位置),通过lower_bound()我们就可快速找到一个数在all数组中的位置,然后储存起来它的下标了。
1 int cnt=unique(all+1,all+tot)-all-1;
2 for (int i=1;i<=n;i++){
3 cun[i].a=lower_bound(all+1,all+cnt+1,cun[i].a)-all;
4 cun[i].b=lower_bound(all+1,all+cnt+1,cun[i].b)-all;
5 }
最后
最后就直接跑并查集就行了。我认为这就不用再说了吧。
完整代码
1 #include<bits/stdc++.h>
2 using namespace std;
3 int all[2000002],fa[20000002];
4 struct u{
5 int a,b,inf;
6 }cun[20000002];
7 bool cmp(u a,u b){
8 return a.inf>b.inf;
9 }
10 int find(int x){
11 if(fa[x]==x) return x;
12 return fa[x]=find(fa[x]);
13 }
14 int main(){
15 ios::sync_with_stdio(false);
16 int t;
17 cin>>t;
18 while(t--){
19 int n,tot=1;
20 cin>>n;
21 memset(all,0,sizeof(all));
22 int judge=0;
23 for (int i=1;i<=n;i++){
24 cin>>cun[i].a>>cun[i].b>>cun[i].inf;
25 if(!cun[i].inf) judge=1;
26 all[tot]=cun[i].a; tot++;
27 all[tot]=cun[i].b; tot++;
28 }
29 if(!judge){cout<<"YES"<<endl;continue;}
30 sort(all+1,all+tot);
31 int cnt=unique(all+1,all+tot)-all-1;
32 for (int i=1;i<=n;i++){
33 cun[i].a=lower_bound(all+1,all+cnt+1,cun[i].a)-all;
34 cun[i].b=lower_bound(all+1,all+cnt+1,cun[i].b)-all;
35 }
36 for (int i=1;i<=cnt+1;i++) fa[i]=i;
37 int js=0;
38 sort(cun+1,cun+1+n,cmp);
39 while(1){
40 js++;
41 if(!cun[js].inf) break;
42 int xx=find(cun[js].a),yy=find(cun[js].b);
43 if(xx!=yy) fa[xx]=yy;
44 }
45 int ans=1;
46 for (int i=js;i<=n;i++){
47 int xx=find(cun[i].a),yy=find(cun[i].b);
48 /*cout<<xx<<' '<<yy<<endl;*/
49 if(xx==yy){
50 ans=0;
51 break;
52 }
53 }
54 if(ans) cout<<"YES"<<endl;
55 else cout<<"NO"<<endl;
56 }
57 return 0;
58 }
结语
这大概是我自学的最好的一回了。unique()和lower_bound()都彻彻底底地查了用法,离散化可以说学清楚了。或许,这就是所谓的“超越教练员”必经的一步吧。
幸甚至哉,歌以咏志。