bzoj4388: JOI2012 invitation(線段樹+堆)

題面在這裏
代碼題。。

題意

n 個男生, m 個女生。給出 k 條關係,每個關係形如 ai,bi,ci,di,ti ,表示 [ai,bi] 的男生和 [ci,di] 的女生是好朋友,幸福指數爲 ti 。現在選擇了一個男生 C 加入集合,接着要每次選擇一個集合中的人的好朋友加入到集合中,並且權值+=他們的幸福指數,直到所有人都被選擇。問怎樣選才能使得幸福指數和最大。若無法將所有人選擇輸出-1

做法

觀察到題目其實在模擬一個prim求最大生成樹的過程。考慮用數據結構優化這個prim。

首先離散化,關鍵點之間的權值和可以直接線段樹維護最大值計算出來,然後將所有點縮爲4n個關鍵點。

開兩個線段樹維護所有未在集合中的點,以及他們連出去的邊。

前者可以保存一個區間最右邊還未選擇的點,如果區間中的數都被選擇就是0;

後者可以對於線段樹每個節點開一個鏈表。

S 爲已選擇的點的集合, T 爲未選擇的點的集合,用一個堆維護 ST 的最大幸福指數,保存那個關係的編號。

每次彈出堆頂,用這個關係不停去更新,直到更新不了爲止。

注意一些細節,在代碼中有相應註釋。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章