zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    背水一战 Windows 10 (61)
    背水一战 Windows 10 (60)
    背水一战 Windows 10 (59)
    背水一战 Windows 10 (58)
    背水一战 Windows 10 (57)
    背水一战 Windows 10 (56)
    背水一战 Windows 10 (55)
    背水一战 Windows 10 (54)
    背水一战 Windows 10 (53)
    背水一战 Windows 10 (52)
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/11745360.html
Copyright © 2011-2022 走看看