題面在這裏
代碼題。。
題意
有 個男生, 個女生。給出 條關係,每個關係形如 ,表示 的男生和 的女生是好朋友,幸福指數爲 。現在選擇了一個男生 加入集合,接着要每次選擇一個集合中的人的好朋友加入到集合中,並且權值+=他們的幸福指數
,直到所有人都被選擇。問怎樣選才能使得幸福指數和最大。若無法將所有人選擇輸出-1
。
做法
觀察到題目其實在模擬一個prim求最大生成樹的過程。考慮用數據結構優化這個prim。
首先離散化,關鍵點之間的權值和可以直接線段樹維護最大值計算出來,然後將所有點縮爲4n個關鍵點。
開兩個線段樹維護所有未在集合中的點,以及他們連出去的邊。
前者可以保存一個區間最右邊還未選擇的點,如果區間中的數都被選擇就是0;
後者可以對於線段樹每個節點開一個鏈表。
設 爲已選擇的點的集合, 爲未選擇的點的集合,用一個堆維護 的最大幸福指數,保存那個關係的編號。
每次彈出堆頂,用這個關係不停去更新,直到更新不了爲止。
注意一些細節,在代碼中有相應註釋。
代碼
#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;
}