BZOJ3716: [PA2014]Muzeum

33 篇文章 0 订阅
32 篇文章 0 订阅

首先把给出的视线范围转成向量,建立以这两个向量为基底的平面直角坐标系,每个点得到新的坐标后取反横坐标,变成每个守卫可以看见横坐标比自己小,纵坐标比自己大的手办

这个东西似乎对应一个最小割模型,加上所有手办的价值,源连守卫权为贿赂守卫的花费,守卫连他能看见的手办权为inf,手办连汇权为手办的代价,减去最小割

因为图太大了,不能跑最小割
考虑到最小割=最大流,可以模型转化,每个守卫有花费这么多的流量,分配给他连的手办,求最大流
因为守卫连边的特殊性,可以贪心,按横坐标从小到大,纵坐标从大到小排序,每个守卫贪心地分配流量给自己能拿的纵坐标最小的手办,易证这样是最优的,写个set就行了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; int f=1;
    while(!((c=getchar())>='0'&&c<='9')) if(c=='-') f=-1;
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
    if(f==-1) x=-x;
}
const int maxn = 810000;
const double eps = 1e-9;

int n,m,N,w,h;
struct node{double x,y;int i,c;}X,Y,a[maxn];
inline bool operator <(const node x,const node y){return x.y-y.y>eps;}
inline bool cmp(const node x,const node y)
{
    if(fabs(x.x-y.x)<eps) return x.y-y.y<-eps;
    return x.x-y.x<-eps;
}

multiset<node>S;
multiset<node>::iterator it,it2;

node generate(int x,int y)
{
    double A=((double)x/w-(double)y/h)/2.0;
    double B=A-(double)x/w;
    return (node){-A,-B,0,0};
}

ll re;

int main()
{
    read(n); read(m); N=n+m;
    read(w); read(h);
    //X=(node){w,-h};
    //Y=(node){-w,-h};

    for(int i=1;i<=n;i++)
    {
        int x,y,c; read(x); read(y); read(c);
        a[i]=generate(x,y); 
        a[i].i=i; a[i].c=c;
        re+=c;
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,c; read(x); read(y); read(c);
        a[n+i]=generate(x,y);
        a[n+i].i=n+i; a[n+i].c=c;
    }
    sort(a+1,a+N+1,cmp);
    for(int i=1;i<=N;i++)
    {
        if(a[i].i<=n) S.insert(a[i]);
        else
        {
            it=S.lower_bound(a[i]);
            while(it!=S.end()&&a[i].c)
            {
                it2=it; it2++;
                if(a[i].c<(*it).c) 
                {
                    node tmp=(*it); tmp.c-=a[i].c; re-=a[i].c;
                    S.erase(it); S.insert(tmp);
                    break;
                }
                a[i].c-=(*it).c; re-=(*it).c; S.erase(it);
                it=it2;
            }
        }
    }
    printf("%lld\n",re);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值