BZOJ4388JOI2012 invitation 堆+线段树+并查集模拟Prim
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4388JOI2012 invitation 堆+线段树+并查集模拟Prim相关的知识,希望对你有一定的参考价值。
【BZOJ4388】JOI2012 invitation
Description
澳洲猴举办了一场宴会,他想要邀请A个男生和B个女生参加,这A个男生从1到A编号,女生也从1到B编号。
现在澳洲猴知道n组朋友关系,这n组朋友关系是这样描述的:男生中编号为Pi到Qi的和女生中编号为Si到Ei的是好朋友,这也就是说(Qi-Pi+1)个男生之间互相是好朋♂友,(Si-Ei+1)个女生之间互相是好朋♂友,(Qi-Pi+1)个男生和(Si-Ei+1)个女生之间互相也是好朋友,总之他们互相是好友关系,并且互相的友好指数为Ti。
现在,澳洲猴只认识一个非常要好的男生C,接着他要进行一系列邀请,使得所有的人都参加这次宴会,邀请的过程是这样的:
选出现有集合中的一个人u(初始时只有C),然后利用这个人u的某个朋友关系i,邀请另一个不在现有集合中的人v进入现有集合,整个集合的幸福值增加Ti。重复直到所有人都进入现有集合,如果不存在将所有人全部邀请的方案,输出-1。
Input
输入的第一行为A,B,C三个正整数。
接下来是n。
然后n行,每行5个正整数,Pi,Qi,Si,Ei,Ti,描述一组朋♂友关系。
Output
输出一行,最大的幸福指数。如果不存在全部邀请的方案,输出-1。
Sample Input
4
2 4 1 3 20
1 2 2 4 40
4 5 2 3 30
4 4 4 6 10
Sample Output
HINT
样例1解释:
男3->男2 +20;
男2->男1 +40;
男2->女1 +20;
男2->女2 +40;
男2->女3 +40;
女2->女4 +40;
女3->男4 +30;
女3->男5 +30;
男4->女5 +10;
男4->女6 +10;
对于100%的数据 n<=100000,1<=A,B,Ti<=1e9,Pi<=Qi<=A,Si<=Ei<=B,C<=A。
题解:思路非常好,也比较显然。
我们分析邀请的过程,如果我们将男生女生看成点,好友关系看成边,Ti看成边权,那么这就是一个Prim算法求最大生成树的过程。所以我们考虑模拟Prim算法,并用数据结构维护一下。
首先,最大生成树以哪个点开始做都无所谓,所以C是没用的。由于A,B很大,我们考虑离散化,得到了4n个区间。我们令每个区间的左端点为这个区间的关键点,然后做Prim时先只对关键点求最大生成树,顺便统计一下覆盖每个区间的Ti的最大值是多少。那么对于非关键点,直接连向这个最大值即可。
接下来是Prim:我们随便在树中加入一个点,然后将所有包含这个点的Ti都放到大根堆中去。每次从堆中拿出Ti最大的关系,然后随便找出一个在当前关系中的,且没有被访问过的点,然后将所有包含这个点的Ti再放到堆中。然后重复以上过程。
如何不重复的找到这些Ti呢?我们对线段树的每个节点都维护一个堆,对于好友关系[l,r],我们将r扔到l所在的堆中,并用线段树维护最大值。这样对于点x,我们不断找出[1,x]中的最大值,如果这个最大值>=x,则将其从线段树中删除(两棵线段树都要删除),并扔到队列中去。
如何找到一个在当前关系中的,且没有被访问过的点呢?用并查集即可。
如何确定覆盖每个区间的Ti最大值呢?用线段树即可。
代码倒是挺长的,难点在离散化~
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <algorithm> #define lson x<<1 #define rson x<<1|1 #define mp(A,B) make_pair(A,B) using namespace std; const int maxn=100010; typedef long long ll; typedef pair<int,int> pii; int C,n,m,M[2],A,B; ll ans; int ref[maxn<<2]; struct node { int a[2],b[2],vis,val; }p[maxn]; struct number { int val,org,k; }num[maxn<<2]; priority_queue<pii> pq; struct heap { priority_queue<pii> p1,p2; inline void push(pii x) {p1.push(x);} inline void erase(pii x) {p2.push(x);} inline int size() {return p1.size()-p2.size();} inline pii top() { while(p2.size()&&p1.top()==p2.top()) p1.pop(),p2.pop(); return p1.size()?p1.top():mp(0,0); } }; struct sag { pii s[maxn<<4]; int f[maxn<<2]; heap q[maxn<<2]; int find(int x) { return (f[x]==x)?x:(f[x]=find(f[x])); } void build(int l,int r,int x) { if(l==r) { s[x]=q[l].top(); return ; } int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); s[x]=max(s[lson],s[rson]); } pii query(int l,int r,int x,int a,int b) { if(a<=l&&r<=b) return s[x]; int mid=(l+r)>>1; if(b<=mid) return query(l,mid,lson,a,b); if(a>mid) return query(mid+1,r,rson,a,b); return max(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b)); } void del(int l,int r,int x,int a,pii b) { if(l==r) { q[l].erase(b); s[x]=q[l].top(); return ; } int mid=(l+r)>>1; if(a<=mid) del(l,mid,lson,a,b); else del(mid+1,r,rson,a,b); s[x]=max(s[lson],s[rson]); } }s[2]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } inline void insert(int y,int x) { //printf("*%d %d\n",y,x); if(!y) A--; else B--; s[y].f[x]=s[y].find(x+1); while(1) { int t=s[y].query(1,M[y],1,1,x).second; if(p[t].b[y]<x) return ; s[0].del(1,M[0],1,p[t].a[0],mp(p[t].b[0],t)),s[1].del(1,M[1],1,p[t].a[1],mp(p[t].b[1],t)); pq.push(mp(p[t].val,t)); } } bool cmp1(number a,number b) { return a.val<b.val; } bool cmp2(node a,node b) { return a.val>b.val; } int main() { //freopen("bz4388.in","r",stdin); A=rd(),B=rd(),rd(),n=rd(); int i,j,u; for(i=1;i<=n;i++) { num[i].val=rd(),num[i+n].val=rd()+1,num[i+2*n].val=rd(),num[i+3*n].val=rd()+1,p[i].val=rd(); num[i].org=num[i+n].org=num[i+2*n].org=num[i+3*n].org=i; num[i].k=0,num[i+n].k=1,num[i+2*n].k=2,num[i+3*n].k=3; } sort(num+1,num+4*n+1,cmp1); for(i=1;i<=4*n;i++) { if(num[i].val>num[i-1].val) ref[++m]=num[i].val; if(num[i].k==0) p[num[i].org].a[0]=m; if(num[i].k==1) p[num[i].org].b[0]=m-1,M[0]=m-1; if(num[i].k==2) p[num[i].org].a[1]=m; if(num[i].k==3) p[num[i].org].b[1]=m-1,M[1]=m-1; } for(i=1;i<=n;i++) s[0].q[p[i].a[0]].push(mp(p[i].b[0],i)),s[1].q[p[i].a[1]].push(mp(p[i].b[1],i)); s[0].build(1,M[0],1),s[1].build(1,M[1],1); for(i=1;i<=M[0]+1;i++) s[0].f[i]=i; for(i=1;i<=M[1]+1;i++) s[1].f[i]=i; insert(0,1); while(!pq.empty()) { u=pq.top().second; int flag=0; for(i=s[0].find(p[u].a[0]);!flag&&i<=p[u].b[0];flag=1) insert(0,i),flag=1; for(i=s[1].find(p[u].a[1]);!flag&&i<=p[u].b[1];flag=1) insert(1,i),flag=1; if(!flag) pq.pop(); else ans+=p[u].val; } sort(p+1,p+n+1,cmp2); for(i=1;i<=M[0]+1;i++) s[0].f[i]=i; for(i=1;i<=M[1]+1;i++) s[1].f[i]=i; for(i=1;i<=n;i++) { for(j=s[0].find(p[i].a[0]);j<=p[i].b[0];j=s[0].find(j)) ans+=(ll)(ref[j+1]-ref[j]-1)*p[i].val,s[0].f[j]=j+1,A-=ref[j+1]-ref[j]-1; for(j=s[1].find(p[i].a[1]);j<=p[i].b[1];j=s[1].find(j)) ans+=(ll)(ref[j+1]-ref[j]-1)*p[i].val,s[1].f[j]=j+1,B-=ref[j+1]-ref[j]-1; } if(A||B) printf("-1"); else printf("%lld",ans); return 0; }
以上是关于BZOJ4388JOI2012 invitation 堆+线段树+并查集模拟Prim的主要内容,如果未能解决你的问题,请参考以下文章