第五章:优先队列、并查集(7.22、7.23)
堆:
模板:
NC16663 合并果子
还是打一下板子吧
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; priority_queue<int, vector<int>, greater<int> > q; int a[10005]; int main() { int n;scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",&a[i]),q.push(a[i]); int ans=0; while(q.size()>=2) { int x=0; x+=q.top();q.pop(); x+=q.top();q.pop(); ans+=x; q.push(x); } printf("%d\n",ans); }
NC214362 第k小
维护size为k的大顶堆
对顶堆:
NC50940 Running Median
经典动态维护序列中位数
大根堆维护较小的一半元素,小根堆维护较大的一半元素
奇数个时,元素更多的那一堆的堆顶为中位数
贪心:
NC20185 [JSOI2010]缓存交换
对于当前的i,如果要清理一个空间,就清理后面最晚出现的那个(贪心)
用nextt[i]和堆维护
可以后悔的贪心:
NC50439 tokitsukaze and Soldier
从大到小枚举s[i]的值。在所有>=s[i]的士兵中选v最大的s[i]个,用优先队列维护。因为s[i]是减小的。所以删除的士兵一定在后面用不到。
map/set:
基本运用:
NC235267 星球大战
虽然是1e9的x和y,但用map就可以解决
注意map怎么写
#include<bits/stdc++.h> using namespace std; const int N=1e5+7; int main() { int n,m;scanf("%d%d",&n,&m); map<int,list<int> >mp; map<int,list<int> >mp1; for(int i=1;i<=n;i++) { int x,y;scanf("%d%d",&x,&y); mp1[x].push_back(y); mp[y].push_back(x); } for(int i=1;i<=m;i++) { int c,d;scanf("%d%d",&c,&d); int cnt=0; if(c==1) { printf("%d\n",mp[d].size()); for(auto i:mp[d])mp1[i].remove(d); mp[d].clear(); } else { printf("%d\n",mp1[d].size()); for(auto i:mp1[d])mp[i].remove(d); mp1[d].clear(); } } return 0; }
并查集:
维护附加信息:
NC235622 叠积木
带权并查集,下面的为父亲
#include<bits/stdc++.h> using namespace std; int fa[30005],siz[30005],d[30005]; int get(int x) { if(x==fa[x])return x; int f=get(fa[x]); d[x]+=d[fa[x]]; fa[x]=f; return fa[x]; } int main() { int T;scanf("%d",&T); for(int i=1;i<=30000;++i)fa[i]=i,siz[i]=1,d[i]=0; while(T--) { getchar(); char c;scanf("%c",&c); if(c=='M') { int x,y;scanf("%d%d",&x,&y); int f1=get(x),f2=get(y); if(f1==f2)continue; fa[f1]=f2; d[f1]=siz[f2]; siz[f2]+=siz[f1]; } else { int x;scanf("%d",&x); get(x); printf("%d\n",d[x]); } } }