可并堆
可并堆,又称为左偏树,满足从一个节点一直向左儿子走比一直向右儿子走距离更长。
这样,它就满足了往右走最多log次,也就是每次合并的时间复杂度为O(log)
合并:将一个合并到另一个的右儿子上,合并的同时满足堆的所有性质。
BZOJ1455罗马游戏:
维护小根堆,每次合并的时候讲大的合并到小的的右儿子上。
可并堆例题,没有什么多说的,权值线段树合并应该也可以做。
附上代码:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <iostream> #include <queue> #include <cstdlib> using namespace std; #define N 1000005 int fa[N],n,Q; char s[2]; struct node { int ls,rs,x,dis; }mp[N]; int merge(int x,int y) { if(!x)return y; if(!y)return x; if(mp[x].x>mp[y].x)swap(x,y); mp[x].rs=merge(mp[x].rs,y); if(mp[mp[x].ls].dis<mp[mp[x].rs].dis)swap(mp[x].ls,mp[x].rs); mp[x].dis=mp[mp[x].rs].dis+1; return x; } int find(int x) { if(fa[x]==x)return x; return fa[x]=find(fa[x]); } bool vis[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { fa[i]=i; scanf("%d",&mp[i].x); } scanf("%d",&Q); while(Q--) { int x,y; scanf("%s%d",s,&x); if(s[0]=='M') { scanf("%d",&y); if(vis[x]||vis[y])continue; int fx=find(x),fy=find(y); if(fx==fy)continue; fa[fx]=fa[fy]=merge(fx,fy); }else { if(vis[x]) { printf("0 "); continue; } int fx=find(x); printf("%d ",mp[fx].x); fa[fx]=fa[mp[fx].ls]=fa[mp[fx].rs]=merge(mp[fx].ls,mp[fx].rs); mp[fx].rs=mp[fx].ls=0; vis[fx]=1; } } return 0; }
BZOJ2809: [Apio2012]dispatching
我们考虑,维护大根堆,费用大于M就弹出堆顶,在给出的树上完成操作,用dfs实现。
附上代码:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <queue> using namespace std; #define N 100005 #define ll long long struct no { int to,next; }e[N]; int head[N],cnt,a[N],b[N],fa[N],n,m; struct node { int ls,rs,x,dis; }mp[N]; void add(int x,int y) { e[cnt].to=y; e[cnt].next=head[x]; head[x]=cnt++; return ; } int find(int x) { if(x==fa[x])return x; return fa[x]=find(fa[x]); } int merge(int x,int y) { if(!x)return y; if(!y)return x; if(mp[x].x<mp[y].x)swap(x,y); mp[x].rs=merge(mp[x].rs,y); if(mp[mp[x].ls].dis<mp[mp[x].rs].dis)swap(mp[x].ls,mp[x].rs); mp[x].dis=mp[mp[x].rs].dis+1; return x; } ll ans,sum[N]; int siz[N]; void dfs(int x,int from) { sum[x]=a[x]; siz[x]=1; for(int i=head[x];i!=-1;i=e[i].next) { int to1=e[i].to; if(to1!=from) { dfs(to1,x); sum[x]+=sum[to1]; int fx=find(x),fy=find(to1); fa[fx]=fa[fy]=merge(fx,fy); siz[x]+=siz[to1]; } } while(sum[x]>m) { siz[x]--; int fx=find(x); sum[x]-=mp[fx].x; mp[fx].x=0; fa[fx]=fa[mp[fx].ls]=fa[mp[fx].rs]=merge(mp[fx].ls,mp[fx].rs); } ans=max(1ll*b[x]*siz[x],ans); return ; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { fa[i]=i; int x; scanf("%d%d%d",&x,&a[i],&b[i]); mp[i].x=a[i]; if(!x)continue; add(x,i); } dfs(1,0); printf("%lld ",ans); return 0; }
BZOJ3011: [Usaco2012 Dec]Running Away From the Barn
这题方法不少,可并堆可以实现,每次将距离大于L的弹出堆顶,同时需要记录堆中元素个数,比较水,记得开long long
附上代码:
#include <cstdio> #include <algorithm> #include <iostream> #include <queue> #include <cmath> #include <cstring> #include <cstdlib> using namespace std; #define N 200005 #define ll long long struct node { int dis,ls,rs; ll x; }mp[N]; struct no { int to,next; ll val; }e[N]; int head[N],cnt; void add(int x,int y,ll z) { e[cnt].to=y; e[cnt].val=z; e[cnt].next=head[x]; head[x]=cnt++; return ; } int fa[N],n,siz[N]; ll dep[N],L; int merge(int x,int y) { if(!x)return y; if(!y)return x; if(mp[x].x<mp[y].x)swap(x,y); mp[x].rs=merge(mp[x].rs,y); if(mp[mp[x].rs].dis>mp[mp[x].ls].dis)swap(mp[x].ls,mp[x].rs); mp[x].dis=mp[mp[x].rs].dis+1; return x; } int find(int x) { if(x==fa[x])return x; return fa[x]=find(fa[x]); } void dfs(int x,int from) { siz[x]=1; for(int i=head[x];i!=-1;i=e[i].next) { int to1=e[i].to; if(to1!=from) { mp[to1].x=dep[to1]=dep[x]+e[i].val; dfs(to1,x); siz[x]+=siz[to1]; int fx=find(x),fy=find(to1); fa[fx]=fa[fy]=merge(fx,fy); } } int fx=find(x); while(mp[fx].x>L+dep[x]) { fa[fx]=fa[mp[fx].ls]=fa[mp[fx].rs]=merge(mp[fx].ls,mp[fx].rs); siz[x]--; fx=find(x); } return ; } int main() { fa[1]=1; memset(head,-1,sizeof(head)); scanf("%d%lld",&n,&L); for(int i=2;i<=n;i++) { fa[i]=i; int x; ll y; scanf("%d%lld",&x,&y); add(x,i,y); } dfs(1,0); for(int i=1;i<=n;i++) { printf("%d ",siz[i]); } return 0; }
BZOJ4003: [JLOI2015]城池攻占
我写过这题的题解,就不重复了,附上链接:https://www.cnblogs.com/Winniechen/p/8890801.html
BZOJ3252: 攻略
网上的题解给的都是线段树+dfs序,挺裸的,可并堆也可以实现,跑的飞起,代码精悍。附上链接:https://www.cnblogs.com/Winniechen/p/8990559.html