【BZOJ】4625 [BeiJing2016]水晶

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4625

首先这东西很明显可以换成一个二维的图……
具体想怎么换怎么换了,我是X对应x+1,Y对应y-1,Z对应x-1 y+1。

考虑每个点对3取膜的余数,可以发现,在0的点周围的6个点中,只要同时存在1和2的点,那么就会引发共振。
那么考虑,对于每个共振,要用最小代价破坏,很显然的一个网络流模型,把每个点拆点,边权为该点价值。然后源连1的点,1连0,0连2,2连汇,总权值减最大流。

#include<stdio.h>
#include<map>
#define cint const int &
#define inf 233333333
#define N 50005
#define M 100005

using namespace std;

int cnt=1,tot,n,sum,s[M],Q[M],ts,ti[M],lev[M],x[N],y[N],z[N],c[N],s1[5],s2[5],l1,l2,cur[M];

struct edge{int v,c,n;}e[N*20];
inline void push(const int &u,const int &v,const int &c)
{
    e[++cnt]=(edge){v,c,s[u]};s[u]=cnt;
    e[++cnt]=(edge){u,0,s[v]};s[v]=cnt;
}

inline bool bfs()
{
    for (int i=1;i<=tot+1<<1;i++) cur[i]=s[i];
    int l=1,r=1;
    ti[Q[1]=1]=++ts;
    for (;l<=r;l++) for (int i=s[Q[l]];i;i=e[i].n) if (ti[e[i].v]<ts && e[i].c) 
        ti[Q[++r]=e[i].v]=ts,lev[e[i].v]=lev[Q[l]]+1;
    return ti[tot+1<<1]==ts;
}

int dfs(const int &u,const int &c)
{
    if (u==tot+1<<1) return c;
    int g=0,f=c,tmp;
    for (int i=cur[u];i && f;cur[u]=i,i=e[i].n) 
    if (ti[e[i].v]==ts && lev[e[i].v]==lev[u]+1 && e[i].c && (tmp=dfs(e[i].v,min(f,e[i].c)))) 
        g+=tmp,f-=tmp,e[i].c-=tmp,e[i^1].c+=tmp; 
    return g;
}

struct poi
{
    int x,y;
    friend bool operator < (poi a,poi b){return a.x==b.x?a.y<b.y:a.x<b.x;}
    friend poi operator + (poi a,poi b){return (poi){a.x+b.x,a.y+b.y};}
}loc[N];

map<poi,int> Map,pt,F,chk;

inline void link(cint p1,cint p2,cint p3)
{
    poi k=(poi){1,p1};
    if (!chk[k]) push(1,p1*2,inf),chk[k]=1;
    k=(poi){p1,p2};
    if (!chk[k]) push(p1*2+1,p2*2,inf),chk[k]=1;
    k=(poi){p2,p3};
    if (!chk[k]) push(p2*2+1,p3*2,inf),chk[k]=1;
    k=(poi){p3,tot+1};
    if (!chk[k]) push(p3*2+1,tot+1<<1,inf),chk[k]=1;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",x+i,y+i,z+i,c+i);
        poi now=(poi){x[i]-z[i],z[i]-y[i]};
        if (!pt[now]) pt[now]=++tot;
        Map[now]+=c[i];
        if ((x[i]+y[i]+z[i])%3==0) F[now]=1;
    }
    for (int i=1;i<=n;i++)
    {
        poi now=(poi){x[i]-z[i],z[i]-y[i]};
        if (F[now]<2)
        {
            int p=pt[now],add=Map[now]*(10+F[now]);sum+=add;
            push(p*2,p*2+1,add);
            if (F[now]==0){F[now]=2;continue;}
            F[now]=2;l1=l2=0;
            if (!(s1[++l1]=pt[now+(poi){0,1}])) l1--;
            if (!(s1[++l1]=pt[now+(poi){-1,0}])) l1--;
            if (!(s1[++l1]=pt[now+(poi){1,-1}])) l1--;
            if (!(s2[++l2]=pt[now+(poi){0,-1}])) l2--;
            if (!(s2[++l2]=pt[now+(poi){1,0}])) l2--;
            if (!(s2[++l2]=pt[now+(poi){-1,1}])) l2--;
            for (int i1=1;i1<=l1;i1++) for (int i2=1;i2<=l2;i2++) link(s1[i1],p,s2[i2]);
        }   
    }
    while (bfs()) for (int rt=dfs(1,inf);rt;rt=dfs(1,inf)) sum-=rt;
    printf("%.1lf",(double)sum/10+0.0001);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值