【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. Django Error: That port is already in use.

    原文:http://stackoverflow.com/questions/20239232/error-that-port-is-already-in-use A more simple solut ...

  2. Linux命令-网络命令:lastlog

    last 显示所有用户最后登录信息(会显示系统用户) last -u 只看某一个用户wangyunpeng的最后登录信息 last -u 查看系统用户root的最后登录信息 root用户的ID是0.从 ...

  3. [Android进阶]Binder学习(初始篇)

    Android中Binder学习(初始篇) 本篇博客学习自侯亮的博客.地址为: 红茶一杯话Binder 1 什么是Binder? 简单地说.Binder是Android平台上的一种跨进程交互技术. 该 ...

  4. 删除MYSQL账号多于的空用户

    默认情况下,mysql安装好之后,会存在匿名用户,也可以叫空用户,输入mysql之后直接回车便可进入mysql. 该匿名用户具有一定的权限,通过SHOW DATABASES;可以查看到informat ...

  5. Jumpserver web界面跳板机

    Jumpserver.org 普通用户 仪表盘 查看主机 上传下载 访问官网 欢迎使用Jumpserver开源跳板机系统 帮助 Log out 查看资产 仪表盘 资产管理 查看资产 主机详细信息列表 ...

  6. 为select的option绑定键盘事件

    1. 目的 可以使用快捷键1.2.3.4等自动选中select框对应的option 2. 代码 <select id="selectItem" class="for ...

  7. Docker Python API 与 Docker Command

    span.kw { color: #007020; font-weight: bold; } code > span.dt { color: #902000; } code > span. ...

  8. 使用sublime模板加快编码效率

    这是使用模板系列的最后一篇了,也是最实用的方法. 前面提到的,插入文件的方法,适合计算机水平一般的初学者:而用TCL脚本的,则适合喜欢自定义各种奇特功能的专业人士. 那么,本次介绍的配置编辑器的方法, ...

  9. [转]手工释放linux内存——/proc/sys/vm/drop_caches

    另一篇:http://www.linuxfly.org/post/320/   1.清理前内存使用情况 free -m 2.开始清理  echo 1 > /proc/sys/vm/drop_ca ...

  10. SQL基础之聚合与排序

    聚合函数是用来求和,平均值,最大最小值一类的函数. 常用的有COUNT.SUM.MAX.MIN.AVG. count() 参数为列名,也可以使用*,表示全部列. 默认*会统计所有行的数据,如果想过滤掉 ...