洛谷 P3377 【模板】左偏树(可并堆)
2019-06-01
题目: 洛谷 P3377 【模板】左偏树(可并堆): https://www.luogu.org/problemnew/show/P3377
题目描述
如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)
操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)
输入输出格式
输入格式:第一行包含两个正整数N、M,分别表示一开始小根堆的个数和接下来操作的个数。
第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。
接下来M行每行2个或3个正整数,表示一条操作,格式如下:
操作1 : 1 x y
操作2 : 2 x
输出格式:输出包含若干行整数,分别依次对应每一个操作2所得的结果。
输入输出样例
5 5 1 5 4 2 3 1 1 5 1 2 5 2 2 1 4 2 2 2
1 2
说明
当堆里有多个最小值时,优先删除原序列的靠前的,否则会影响后续操作1导致WA。
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=1000,M<=1000
对于100%的数据:N<=100000,M<=100000
样例说明:
初始状态下,五个小根堆分别为:{1}、{5}、{4}、{2}、{3}。
第一次操作,将第1个数所在的小根堆与第5个数所在的小根堆合并,故变为四个小根堆:{1,3}、{5}、{4}、{2}。
第二次操作,将第2个数所在的小根堆与第5个数所在的小根堆合并,故变为三个小根堆:{1,3,5}、{4}、{2}。
第三次操作,将第2个数所在的小根堆的最小值输出并删除,故输出1,第一个数被删除,三个小根堆为:{3,5}、{4}、{2}。
第四次操作,将第4个数所在的小根堆与第2个数所在的小根堆合并,故变为两个小根堆:{2,3,5}、{4}。
第五次操作,将第2个数所在的小根堆的最小值输出并删除,故输出2,第四个数被删除,两个小根堆为:{3,5}、{4}。
故输出依次为1、2。
首先,这道题目标题便很明确地告诉我们——这是一道模板题。
是模板题,就应该从理论出发,普及常识,最后得到一长串代码(疯狂背诵)
但在这里,我是不会讲左偏树的基本姿势滴..(其实我也不会),如有需要,自行百度~~~~~~~~~~
我在这里介绍的是“pbds”(平板电视)的解法?
那么显而易见,这道题一下子没有了难度。或许有些童鞋已经参照 pbds的姿势做出来了。
既然大家都会了,那么直接上代码:
1 // 2 #include <bits/stdc++.h> 3 #include <ext/pb_ds/priority_queue.hpp> 4 using namespace std; 5 using namespace __gnu_pbds; 6 typedef unsigned long long ll; 7 #define ri register ll 8 9 struct A 10 { 11 ll x; 12 ll rank; 13 bool operator< (const A& p)const 14 { 15 if(x==p.x) 16 { 17 return rank>p.rank; 18 } 19 return x>p.x; 20 // */ 21 } 22 }; 23 24 __gnu_pbds::priority_queue<A,less<A>,pairing_heap_tag> q[100005]; 25 ll n,m; 26 ll father[100005]; 27 bool vis[100005]; 28 29 ll findf(ll a) 30 { 31 ri k=a; 32 while(father[k]!=k) 33 { 34 k=father[k]; 35 } 36 father[a]=k; 37 return k; 38 } 39 40 void joinf(ll x,ll y) 41 { 42 ri xx=findf(x); 43 ri yy=findf(y); 44 father[yy]=xx; 45 } 46 47 signed main() 48 { 49 ios::sync_with_stdio(0),cin.tie(0); 50 cin>>n>>m; 51 for(ri i=1;i<=n;i++) 52 { 53 ri p;cin>>p; 54 q[i].push((A){p,i}); 55 father[i]=i; 56 } 57 58 for(ri i=1;i<=m;i++) 59 { 60 ri rerinput;cin>>rerinput; 61 if(rerinput==1) 62 { 63 ri x,y;cin>>x>>y; 64 ri xx=findf(x); 65 ri yy=findf(y); 66 if(q[xx].size()>q[yy].size())swap(xx,yy); 67 q[xx].join(q[yy]); 68 joinf(xx,yy); 69 } 70 else if(rerinput==2) 71 { 72 ri k;cin>>k; 73 if(vis[k]) 74 { 75 cout<<-1<<'\n'; 76 continue; 77 } 78 ri wh=findf(k); 79 cout<<q[wh].top().x<<'\n'; 80 vis[q[wh].top().rank]=1; 81 q[wh].pop(); 82 } 83 } 84 85 // */ 86 return 0; 87 } 88 //
然后开森的得到下面的结果:
好好好。
我的错,我的错。
一定又是哪里让我给遗漏了。
那再重审一次题目:我发现我遗漏了操作一后面小括号里的内容。
于是重新改一次,增加了66,67行的内容
1 // 2 #include <bits/stdc++.h> 3 #include <ext/pb_ds/priority_queue.hpp> 4 using namespace std; 5 using namespace __gnu_pbds; 6 typedef unsigned long long ll; 7 #define ri register ll 8 9 struct A 10 { 11 ll x; 12 ll rank; 13 bool operator< (const A& p)const 14 { 15 if(x==p.x) 16 { 17 return rank>p.rank; 18 } 19 return x>p.x; 20 // */ 21 } 22 }; 23 24 __gnu_pbds::priority_queue<A,less<A>,pairing_heap_tag> q[100005]; 25 ll n,m; 26 ll father[100005]; 27 bool vis[100005]; 28 29 ll findf(ll a) 30 { 31 ri k=a; 32 while(father[k]!=k) 33 { 34 k=father[k]; 35 } 36 father[a]=k; 37 return k; 38 } 39 40 void joinf(ll x,ll y) 41 { 42 ri xx=findf(x); 43 ri yy=findf(y); 44 father[yy]=xx; 45 } 46 47 signed main() 48 { 49 ios::sync_with_stdio(0),cin.tie(0); 50 cin>>n>>m; 51 for(ri i=1;i<=n;i++) 52 { 53 ri p;cin>>p; 54 q[i].push((A){p,i}); 55 father[i]=i; 56 } 57 58 for(ri i=1;i<=m;i++) 59 { 60 ri rerinput;cin>>rerinput; 61 if(rerinput==1) 62 { 63 ri x,y;cin>>x>>y; 64 ri xx=findf(x); 65 ri yy=findf(y); 66 if(vis[x]||vis[y])continue; 67 if(xx==yy)continue; 68 if(q[xx].size()>q[yy].size())swap(xx,yy); 69 q[xx].join(q[yy]); 70 joinf(xx,yy); 71 } 72 else if(rerinput==2) 73 { 74 ri k;cin>>k; 75 if(vis[k]) 76 { 77 cout<<-1<<'\n'; 78 continue; 79 } 80 ri wh=findf(k); 81 cout<<q[wh].top().x<<'\n'; 82 vis[q[wh].top().rank]=1; 83 q[wh].pop(); 84 } 85 } 86 87 // */ 88 return 0; 89 } 90 //
然后愉快地过了。↑↑↑↑↑↑↑↑↑↑AC代码↑↑↑↑↑↑↑↑↑↑
但是!!!
-----看一看题解。
dalao们已经讲得很详细了(根本没看懂)。
从茫茫代码里,我发现了一个重要的一句话——————
——————dalao又做粗了结论性的发言
我一下子豁然开朗,去掉了上面代码里的路径压缩
结果:
额~~好吧,我又不知道是为什么了
但我这个代码似乎是需要路径压缩滴。
小结:
这篇随笔告诉我们——看题要仔细,题目给出的限制一定要考虑!
除此外再AC代码第68行:“if(q[xx].size()>q[yy].size())swap(xx,yy);”这个似乎必须 q[xx].size() < q[yy].size()。
但实话说,我也不是很清楚,
如果有dalao知道,还望指教~~