【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;
}

【BZOJ4388】JOI2012 invitation 堆+线段树+并查集(模拟Prim)的更多相关文章

  1. [WC2005]双面棋盘(线段树+并查集)

    线段树+并查集维护连通性. 好像 \(700ms\) 的时限把我的常数超级大的做法卡掉了, 必须要开 \(O_2\) 才行. 对于线段树的每一个结点都开左边的并查集,右边的并查集,然后合并. \(Co ...

  2. 2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集)

    2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集) https://www.luogu.com.cn/problem/CF811E Ste ...

  3. 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status ...

  4. 【XSY2707】snow 线段树 并查集

    题目描述 有\(n\)个人和一条长度为\(t\)的线段,每个人还有一个工作范围(是一个区间).最开始整条线段都是白的.定义每个人的工作长度是这个人的工作范围中白色部分的长度(会随着线段改变而改变).每 ...

  5. bzoj 2054: 疯狂的馒头(线段树||并查集)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2054 线段树写法: 点的颜色只取决于最后一次染的颜色,所以我们可以倒着维护,如果当前区间之前 ...

  6. 【CF687D】Dividing Kingdom II 线段树+并查集

    [CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两 ...

  7. 【BZOJ1453】[Wc]Dface双面棋盘 线段树+并查集

    [BZOJ1453][Wc]Dface双面棋盘 Description Input Output Sample Input Sample Output HINT 题解:话说看到题的第一反应其实是LCT ...

  8. codeforces 811E Vladik and Entertaining Flags(线段树+并查集)

    codeforces 811E Vladik and Entertaining Flags 题面 \(n*m(1<=n<=10, 1<=m<=1e5)\)的棋盘,每个格子有一个 ...

  9. 【Codeforces811E】Vladik and Entertaining Flags [线段树][并查集]

    Vladik and Entertaining Flags Time Limit: 20 Sec  Memory Limit: 512 MB Description n * m的矩形,每个格子上有一个 ...

随机推荐

  1. Python-绑定与未绑定方法通俗讲解

    像函数一样,Python中的类方法也是一种对象.由于既可以通过实例也可以通过类来访问方法,所以在Python里有两种风格:   未绑定的类方法:没有self     通过类来引用方法返回一个未绑定方法 ...

  2. linux下各种形式的shell加法操作总结

    linux 下shell加法操作总结: #!/bin/bash   n=1;echo -n "$n "   let "n = $n + 1" echo -n & ...

  3. HTTP协议,详解

    整合网上各种资料,原创,不懂可以加 QQ:3111901846 一般学习一样新的知识,你首先要问问自己这三个问题,如果学完以后,你能回答出来这几个问题,证明你还是不错的 1.什么是HTTP协议?2.H ...

  4. jquery ajax、get、post实例

    .post.$.get是一些简单的方法,如果要处理复杂的逻辑,还是需要用到jQuery.ajax() 一.$.ajax的一般格式 $.ajax({ type: 'POST', url: url , d ...

  5. PHP之AOP思想

    故事背景: 问题: 在传统的OOP(面向对象编程:Object-Oriented Programming)思想里,一般把应用程序分解成若干个的对象,强调高内聚,弱耦合,从而提高应用程序的模块化程度,但 ...

  6. 在Visual Studio 2013顯示SCSS詳細錯誤訊息

    在WebEssentials套件加持之下,Visual Studio 2013可以直接編修SCSS,每次存檔自動編譯出css.min.css及.map,非常方便.但初心者如我,寫錯語法在所難免,一旦造 ...

  7. [svc][bg]phabricator-zh_CN汉化包

    汉化phabricator审计系统 主要是用来审计一些开发bug的,客服会提交一些bug,测试也会提交一些bug给开发. https://github.com/wanthings/phabricato ...

  8. 分析并实现 360 P1路由器上的朋友专享网络 功能

    笔者分析了360 P1路由器上的朋友专享网络功能,发现其主要由如下子功能组成: 1. APP点击“立即开启”,则路由器会多出一个新的SSID:360朋友专享网络-8463.此SSID不加密:同时,原有 ...

  9. VMware网络连接失败解决方法

    假如你碰到了VMware 网络被断开,明明已经分配了适配器,客户端却显示网络断开没有连接. 一. 可用恢复默认的方法重置所有网卡及服务. 如图片操作: 进主工具首页.点击: 虚拟网络编辑器 然后点击下 ...

  10. 两款 REST 测试工具

    用CURL命令行测试REST API 无疑是低效率的,这里把最近使用的两款 Chrome 插件总结下 POSTMAN 简单易用 REST Console 功能强大 使用的话用POSTMAN就够用了,但 ...