[bzoj3716]Muzeum

題目大意

商場是一個平面直角座標系。
lihua想要來偷珠寶,一共有n 個珠寶,分佈在不同的位置,第i 個珠寶在(axi,ayi) 有價值爲avi
但是有m 個保安,第i 個保安在(bxi,byi) ,lihua賄賂他需要bvi 的代價。
每個保安都有一個相同的視野角度θ。
如圖:
這裏寫圖片描述
lihua可以花費代價來賄賂保安。
如果一個珠寶不在任何一個未被賄賂保安視野範圍內,lihua可以偷走它獲得對應價值。
求lihua最大收益。

最小割

首先顯然可以最小割。
我們要最大化收益,那麼可以求珠寶的價值總和,然後考慮最小化無法偷走的珠寶價值和以及需要賄賂的保安代價和。
對每個保安和珠寶都建立一個點。
如果一個保安點屬於S集合,規定其未被賄賂。
如果一個珠寶點屬於S集合,規定其無法偷走。
根據這兩條規定,容易建圖,保安點放左側,源點向保安點連代價。珠寶點放右側,珠寶點向匯點連價值。
因爲一個保安如果沒被賄賂,那麼其能看到的珠寶則無法偷走。
因此每個保安點向能看到的珠寶點連正無窮的邊,表示保安點屬於S集合(未被賄賂),則能看到的珠寶點也應屬於S集合(無法偷走)。

貪心

最小割是等於最大流的。
我們考慮模型轉換:
保安點i 是一個噴水的,能噴bvi 的水。
珠寶點i 是一個儲水的,最多儲avi 的水。
一個噴水的可以向視野內的儲水裝置噴水。
這確實是網絡流。
現在要知道最多噴多少水。
不妨先拉伸座標系使得視野呈直角。
然後再45度旋轉:
這裏寫圖片描述
可以發現這樣改變座標系後,視野範圍更好定義。
現在我們將珠寶和保安放在一起,按x 座標從大到小做。
插入一個保安時,需要決策噴水。
由於我們按x 座標有序做,因此只需要考慮y 座標即可。
既然如此,我們可以貪心的想,先噴y 座標最小的,噴滿了繼續噴第二小的,直至沒有可以噴的或者噴完了水。
用set即可操作。

#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=400000+10;
const ll inf=1000000000000000000;
const db eps=1e-10;
db x[maxn],y[maxn];
int v[maxn],id[maxn];
int i,j,k,l,t,n,m;
db W,H,nx,ny;
ll ans;
struct compare {
    bool operator () (int i, int j) {
        return y[i] < y[j] || (y[i] == y[j] && i < j);
    }
};
set<int,compare> s;
set<int,compare>::iterator it,ut;
bool cmp(int a,int b){
    return x[a]<x[b];
}
int main(){
    scanf("%d%d",&n,&m);
    scanf("%lf%lf",&W,&H);
    fo(i,1,n+m){
        scanf("%lf%lf%d",&x[i],&y[i],&v[i]);
        if (H<W) x[i]*=H/W;
        else y[i]*=W/H;
        nx=x[i]+y[i];ny=y[i]-x[i];
        x[i]=nx;y[i]=ny;
        if (i>n) x[i]+=eps,y[i]+=eps;
    }
    fo(i,1,n+m) id[i]=i;
    sort(id+1,id+n+m+1,cmp);
    fo(i,1,n) ans+=(ll)v[i];
    y[n+m+1]=inf;
    s.insert(n+m+1);
    y[0]=-inf;
    s.insert(0);
    fo(i,1,n+m){
        j=id[i];
        if (j<=n) s.insert(j);
        else{
            it=s.upper_bound(j);
            for (--it;*it;it=ut) {
                ut=it,--ut;
                k=min(v[*it],v[j]);
                v[*it]-=k;
                v[j]-=k;
                ans-=(ll)k;
                if (!v[*it]) s.erase(it);
                else break;
            }
        }
    }
    printf("%lld\n",ans);
}
發佈了836 篇原創文章 · 獲贊 317 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章