【JOISC2012 / bzoj4388】Invitation

Description

  linkc++

Solution

  能夠發現題目在模擬 Prim 算法求最大生成樹的過程,樹邊故答案與起點 \(C\) 無關。
  先把全部區間離散化,注意對於一個區間 \([l,r]\),要把 \(l,l+1,r\) 三個位置提出來離散化,做爲全部新區間的左端點,這樣總共只有 \(O(n)\) 個新區間。提 \(l+1\) 的緣由待會再說。
  則從起點 \(C\) 出發,尋找全部覆蓋 \(C\) 的區間,將這個區間連向的另外一個區間扔進大根堆,鍵值爲邊權,並刪除這個區間。每次取出大根堆堆頂的區間,尋找該區間最左邊的一個未被邀請的點,再從該點出發尋找全部覆蓋該點的區間,以此類推。
  爲何要把每一個區間的左端點單獨拆成一個新點(即離散化時要把 \(l+1\) 提出來)呢?考慮這麼一種狀況:對於一對相連區間,設這對區間中沒有子區間,當從外部第一次更新到 這對區間中的某一個區間 時,該區間內的全部點的答案可能不一樣,即外部先邀請到這對區間的任意一個左端點,而後該左端點藉助其和 這對區間中的另外一個區間 的連邊,通過另外一個區間,而後再走若干步,最後回到這個區間時可能會有更大的答案。若是不把左端點拆出來的話,第一次更新到 這對區間中的某一個區間 時,算出來的就是沒考慮這對區間的連邊時的答案(咱們已經假設這對區間中沒有子區間,故原來的 \([l,r]\) 區間在離散化後必定是一個點,答案被認爲是統一的),因此答案可能會變小。git

  而後考慮模擬細節:
    尋找全部覆蓋第 \(x\) 個位置的區間。先把全部區間的編號和右端點合爲一個二元組 扔到其左端點上,而後把每一個點上全部二元組按右端點從大到小排序,查詢時枚舉第 \(1\)\(x\) 個位置,對每一個位置從前日後一直取出並刪除二元組,取出全部右端點 \(\ge x\) 的二元組,每一個二元組就對應一個覆蓋第 \(x\) 個位置的區間。
    查找某個區間 \([l,r]\) 最左邊的一個未被邀請的點。用並查集,初始時每一個點的父親都是本身,一個點被邀請則將父親設爲右側點。這樣直接 \(find(l)\) 就找到了。算法

  複雜度 \(O(n\log n)\)(刪除每一個區間的最壞時間是 \(O(\log n)\)ui

#include<bits/stdc++.h>
#define ll long long
const int N = (int)1e5 + 5;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read(){
    int x=0; bool f=1; char c=getchar();
    for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
    for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    if(f) return x; return 0-x;
}
int A,B,n,cnt,num[N*6],len[N*6],sum; ll ans;
struct info{int l1,r1,l2,r2,v;} a[N];
struct seg{
    int l,r,v;
    inline bool operator < (const seg& a)const{
        return v<a.v;
    }
};
priority_queue<seg> Q;
int f[N*6];
int find(int x) {return x==f[x] ? x : f[x]=find(f[x]);}
bool vis[N]; 
void add_group(int x){
    if(vis[x]) return;
    vis[x]=1;
    Q.push((seg){a[x].l1,a[x].r1,a[x].v}),
    Q.push((seg){a[x].l2,a[x].r2,a[x].v});
}
namespace SegTree{
    #define ls o<<1
    #define rs o<<1|1
    vector<pii> vec[N*24];
    int s[N*24],p[N*24];
    void ins(int o, int l, int r, int x, int v1, int v2){
        if(l==r) {vec[o].pb(mp(v1,v2)); return;}
        int mid=l+r>>1;
        if(x<=mid) ins(ls,l,mid,x,v1,v2);
        else ins(rs,mid+1,r,x,v1,v2);
    }
    inline void pushup(int o) {s[o] = max(s[ls],s[rs]);} 
    void build(int o, int l, int r){
        if(l==r){
            if(vec[o].empty()) s[o] = -1;
            else
                sort(vec[o].begin(),vec[o].end(),greater<pii>()),
                s[o] = vec[o][0].fi;
            return;
        }
        int mid=l+r>>1;
        build(ls,l,mid), build(rs,mid+1,r);
        pushup(o);
    }
    void mdf(int o, int l, int r, int x){
        if(s[o]<x) return;
        if(l==r){
            while(p[o]<vec[o].size() && vec[o][p[o]].fi>=x) add_group(vec[o][p[o]++].se);
            s[o] = (p[o]<vec[o].size() ? vec[o][p[o]].fi : -1);
            return;
        }
        int mid=l+r>>1;
        mdf(ls,l,mid,x);
        if(x>mid) mdf(rs,mid+1,r,x);
        pushup(o);
    }
    #undef ls
    #undef rs
} using namespace SegTree;
void invite(int x, int v){
    f[x] = x+1;
    mdf(1,1,cnt,x);
    sum += len[x];
    ans += (ll)len[x]*v;
}
inline int lsh(int x) {return upper_bound(num+1,num+cnt+1,x)-num-1;} 
int main(){ 
    A=read(), B=read(), read(), n=read();
    for(int i=1; i<=n; ++i){
        a[i].l1=read(), a[i].r1=read(), a[i].l2=read()+A, a[i].r2=read()+A, a[i].v=read();
        num[++cnt]=a[i].l1, num[++cnt]=a[i].l1+1, num[++cnt]=a[i].r1+1, num[++cnt]=a[i].l2, num[++cnt]=a[i].l2+1, num[++cnt]=a[i].r2+1;
    }
    num[++cnt]=1, num[++cnt]=2;
    sort(num+1,num+cnt+1), cnt=unique(num+1,num+cnt+1)-num-1;
    for(int i=1; i<=n; ++i){
        a[i].l1 = lsh(a[i].l1),
        a[i].r1 = lsh(a[i].r1),
        a[i].l2 = lsh(a[i].l2), 
        a[i].r2 = lsh(a[i].r2);
    }
    --cnt;
    for(int i=1; i<=cnt; ++i) len[i] = num[i+1]-num[i];
    for(int i=1; i<=cnt+1; ++i) f[i] = i;
    for(int i=1; i<=n; ++i) ins(1,1,cnt,a[i].l1,a[i].r1,i),
                            ins(1,1,cnt,a[i].l2,a[i].r2,i);
    build(1,1,cnt);
    invite(1,0); seg tmp; int x;
    while(!Q.empty()){
        tmp = Q.top(), Q.pop();
        x = find(tmp.l);
        if(x<=tmp.r) invite(x,tmp.v), Q.push(tmp);
    }
    if(sum==A+B) printf("%lld\n",ans);
    else printf("-1\n");
    return 0;
}
相關文章
相關標籤/搜索