[离散化模板][洛谷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 } 
View Code

结语

 这大概是我自学的最好的一回了。unique()和lower_bound()都彻彻底底地查了用法,离散化可以说学清楚了。或许,这就是所谓的“超越教练员”必经的一步吧。

 幸甚至哉,歌以咏志。

posted @ 2020-03-09 16:41  DarthVictor  阅读(204)  评论(1编辑  收藏  举报
莫挨老子!