• 【JOISC2012 / bzoj4388】Invitation


    Description

      link

    Solution

      可以发现题目在模拟 Prim 算法求最大生成树的过程,树边故答案与起点 (C) 无关。
      先把所有区间离散化,注意对于一个区间 ([l,r]),要把 (l,l+1,r) 三个位置提出来离散化,作为所有新区间的左端点,这样总共只有 (O(n)) 个新区间。提 (l+1) 的原因待会再说。
      则从起点 (C) 出发,寻找所有覆盖 (C) 的区间,将这个区间连向的另一个区间扔进大根堆,键值为边权,并删除这个区间。每次取出大根堆堆顶的区间,寻找该区间最左边的一个未被邀请的点,再从该点出发寻找所有覆盖该点的区间,以此类推。
      为什么要把每个区间的左端点单独拆成一个新点(即离散化时要把 (l+1) 提出来)呢?考虑这么一种情况:对于一对相连区间,设这对区间中没有子区间,当从外部第一次更新到 这对区间中的某一个区间 时,该区间内的所有点的答案可能不同,即外部先邀请到这对区间的任意一个左端点,然后该左端点借助其和 这对区间中的另一个区间 的连边,经过另一个区间,然后再走若干步,最后回到这个区间时可能会有更大的答案。如果不把左端点拆出来的话,第一次更新到 这对区间中的某一个区间 时,算出来的就是没考虑这对区间的连边时的答案(我们已经假设这对区间中没有子区间,故原来的 ([l,r]) 区间在离散化后一定是一个点,答案被认为是统一的),所以答案可能会变小。

      然后考虑模拟细节:
        寻找所有覆盖第 (x) 个位置的区间。先把所有区间的编号和右端点合为一个二元组 扔到其左端点上,然后把每个点上所有二元组按右端点从大到小排序,查询时枚举第 (1)(x) 个位置,对每个位置从前往后一直取出并删除二元组,取出所有右端点 (ge x) 的二元组,每个二元组就对应一个覆盖第 (x) 个位置的区间。
        查找某个区间 ([l,r]) 最左边的一个未被邀请的点。用并查集,初始时每个点的父亲都是自己,一个点被邀请则将父亲设为右侧点。这样直接 (find(l)) 就找到了。

      复杂度 (O(nlog n))(删除每个区间的最坏时间是 (O(log n))

    #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
    ",ans);
        else printf("-1
    ");
        return 0;
    }
    
  • 相关阅读:
    iOS 界面翻转切换动画
    深度解析Objective-C笔试题
    Objective-C内存管理基础
    Objective-C入门教材
    Objective-C代码学习大纲(6)
    Objective-C代码学习大纲(5)
    Objective-C代码学习大纲(4)
    sharedPreferences
    ListView判断滑动底部
    Oracle 游标疑问
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/11745360.html
Copyright © 2020-2023  润新知