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

5 6 3
4
2 4 1 3 20
1 2 2 4 40
4 5 2 3 30
4 4 4 6 10

Sample Output

280

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的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4247:挂饰

bzoj4247挂饰 背包dp

bzoj4240

bzoj4241 历史研究

[BZOJ4241]历史研究

[bzoj4247][挂饰] (动规+排序)