题面在这里
代码题。。
题意
有
n
n
个男生, 个女生。给出
k
k
条关系,每个关系形如 ,表示
[ai,bi]
[
a
i
,
b
i
]
的男生和
[ci,di]
[
c
i
,
d
i
]
的女生是好朋友,幸福指数为
ti
t
i
。现在选择了一个男生
C
C
加入集合,接着要每次选择一个集合中的人的好朋友加入到集合中,并且权值+=他们的幸福指数
,直到所有人都被选择。问怎样选才能使得幸福指数和最大。若无法将所有人选择输出-1
。
做法
观察到题目其实在模拟一个prim求最大生成树的过程。考虑用数据结构优化这个prim。
首先离散化,关键点之间的权值和可以直接线段树维护最大值计算出来,然后将所有点缩为4n个关键点。
开两个线段树维护所有未在集合中的点,以及他们连出去的边。
前者可以保存一个区间最右边还未选择的点,如果区间中的数都被选择就是0;
后者可以对于线段树每个节点开一个链表。
设 为已选择的点的集合, T T 为未选择的点的集合,用一个堆维护 的最大幸福指数,保存那个关系的编号。
每次弹出堆顶,用这个关系不停去更新,直到更新不了为止。
注意一些细节,在代码中有相应注释。
代码
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x); i<=(y); i++)
#define N 131072
#define M 2097152
#define ll long long
#define pii pair<int,int>
#define lc (o<<1)
#define rc (o<<1|1)
using namespace std;
ll read(){
char ch=getchar(); ll x=0; int op=1;
for (; !isdigit(ch); ch=getchar()) if (ch=='-') op=-1;
for (; isdigit(ch); ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*op;
}
int n1,C,seq[N<<1],val[M]; ll ans; bool del[N];
void gg(){
puts("-1"); exit(0);
}
priority_queue<pii> q;
struct node{ int a,b,c,d,t; }d[N];
void cmax(int &x,int y){
if (y>x) x=y;
}
struct seg{
int n,m,cnt,b[N<<1],val[M],head[M];
seg(){
cnt=0; m=0;
memset(head,0,sizeof(head));
}
struct linker{
int to,nxt;
}e[M];
void add(int x,int y){
b[++m]=x; b[++m]=y;
}
void pre(){
b[++m]=1; b[++m]=n; sort(b+1,b+1+m);
int m_=m; m=0;
rep (i,1,m_) if (!m || b[i]!=b[m]) b[++m]=b[i];
}
void get(int &x,int &y){
x=lower_bound(b+1,b+1+m,x)-b;
y=lower_bound(b+1,b+1+m,y)-b;
}
void cal(){
rep (i,1,m-1) if (b[i+1]-b[i]-1){
if (!seq[i]) gg();
ans+=(ll)seq[i]*(b[i+1]-b[i]-1);
}
}
void build(int o,int l,int r){
val[o]=r; if (l==r) return;
int mid=l+r>>1;
build(lc,l,mid); build(rc,mid+1,r);
}
void ins(int o,int l,int r,int x,int y,int z){
if (x<=l && r<=y){
e[++cnt].to=z; e[cnt].nxt=head[o]; head[o]=cnt;
return;
}
int mid=l+r>>1;
if (x<=mid) ins(lc,l,mid,x,y,z);
if (y>mid) ins(rc,mid+1,r,x,y,z);
}
void change(int o,int l,int r,int p){
for (int &i=head[o]; i; i=e[i].nxt){//一定要加‘&’!这样才不会重复遍历。
int v=e[i].to;
if (!del[v]){
q.push(make_pair(d[v].t,v));
del[v]=1;
}
}
if (l==r){ val[o]=0; return; }
int mid=l+r>>1;
if (p<=mid) change(lc,l,mid,p); else change(rc,mid+1,r,p);
val[o]=max(val[lc],val[rc]);
}
int ask(int o,int l,int r,int x,int y){
if (x<=l && r<=y) return val[o];
int mid=l+r>>1,ret=0;
if (x<=mid) cmax(ret,ask(lc,l,mid,x,y));
if (y>mid) cmax(ret,ask(rc,mid+1,r,x,y));
return ret;
}
}A,B;//A,B存的是所有未在集合中的点,以及他们连出去的边
void build(int o,int l,int r){
val[o]=0; if (l==r) return;
int mid=l+r>>1;
build(lc,l,mid); build(rc,mid+1,r);
}
void change(int o,int l,int r,int x,int y,int v){
if (x<=l && r<=y){
cmax(val[o],v); return;//标记永久化
}
int mid=l+r>>1;
if (x<=mid) change(lc,l,mid,x,y,v);
if (y>mid) change(rc,mid+1,r,x,y,v);
}
void dfs(int o,int l,int r){
if (l==r){ seq[l]=val[o]; return; }
int mid=l+r>>1;
cmax(val[lc],val[o]); cmax(val[rc],val[o]);
dfs(lc,l,mid); dfs(rc,mid+1,r);
}
int main(){
A.n=read(); B.n=read(); C=read();
n1=read();
rep (i,1,n1){
d[i].a=read(); d[i].b=read(); d[i].c=read(); d[i].d=read(); d[i].t=read();
A.add(d[i].a,d[i].b); B.add(d[i].c,d[i].d);
}
A.pre();//离散化
rep (i,1,n1) A.get(d[i].a,d[i].b);
build(1,1,A.m);
rep (i,1,n1) if (d[i].a<d[i].b) change(1,1,A.m,d[i].a,d[i].b-1,d[i].t);
dfs(1,1,A.m); A.cal();
B.pre();//离散化
rep (i,1,n1) B.get(d[i].c,d[i].d);
build(1,1,B.m);
rep (i,1,n1) if (d[i].c<d[i].d) change(1,1,B.m,d[i].c,d[i].d-1,d[i].t);
dfs(1,1,B.m); B.cal();
//以上处理了相邻关键点之间的权值
A.build(1,1,A.m); B.build(1,1,B.m);
rep (i,1,n1){
A.ins(1,1,A.m,d[i].a,d[i].b,i);//建立每个点对应的关系
B.ins(1,1,B.m,d[i].c,d[i].d,i);
}
A.change(1,1,A.m,C);
while (!q.empty()){
int x=q.top().second,y; q.pop();
if (y=A.ask(1,1,A.m,d[x].a,d[x].b)){
ans+=d[x].t; A.change(1,1,A.m,y);
q.push(make_pair(d[x].t,x));//一直要push进去,直到没有可以更新的人为止
continue;
}
if (y=B.ask(1,1,B.m,d[x].c,d[x].d)){
ans+=d[x].t; B.change(1,1,B.m,y);
q.push(make_pair(d[x].t,x));
}
}
if (A.val[1] || B.val[1]) gg();
printf("%lld\n",ans);
return 0;
}