bzoj1061

这真真真是一道超超超巧妙的题

首先拿到这道题,我考虑假设每个人需要x[i]个,那么我们就可以n个如下式子:

a1*x[1]+a2*x[2]+...+am*x[m]>=day1num

a1'*x[1]+a2'*x[2]+......+am'*x[m]>=day2num

.......

其中若第i个人对某天有贡献则a[i]=1 否则 a[i]=0

然后我就跑偏了......

我考虑这是一个n维空间,那么这就相当于一个n维空间上的线性规划问题

然后就不知道怎么在n维空间上做线性规划.....

后来明白正解是网络流

以下正解:

样例:

3 3
2 3 4
1 2 2
2 3 5
3 3 2

我们考虑第i种人需要x[i]个,那么可以得到以下不等式:

x[1]>=2;

x[1]+x[2]>=3;

x[2]+x[3]>=4;

我们设变量y[i]满足:

0=0;                       1

x[1]-y[1]=2;               2

x[1]+x[2]-y[2]=3;      3

x[2]+x[3]-y[3]=4;      4

0=0;                          5

然后将2式-1式,3式-2式,4式-3式,5式-4式可得

x[1]-y[1]=2;

x[2]+y[1]-y[2]=1;

x[3]-x[1]+y[2]-y[3]=1

-x[2]-x[3]+y[3]=-4

观察这三个式子,巧妙的是这三个式子中,x[i],y[i]都出现了2次,且前面的系数为+1和-1

这个式子很想网络流的流量平衡吧

所以我们可以将这n+1个式子看成n+1个点,再建立源点和汇点s,t;

1.对于等式右边c大于0的点,我们将其与s连一条容量为c,距离(费用)为0的边

2.对于等式右边c小于0的点,我们将其与t连一条容量为-c,距离(费用)为0的边

3.对于每个x[i],连一条x[i]指向-x[i]的容量为inf,距离为cost[i]的边

4.对于每个y[i],连一条y[i]指向-y[i]的容量为inf,距离为0的边

然后拿这个图跑一跑s->t的费用流,所得的最小费用就是答案

那么为什么这么建图呢?我们逐条解释

对于1,2因为我们把每个式子看成了一个点,那么该点是流量平衡的(即该等式成立),那么左边代表流进流出,右边恒等于一常数c,那么这个c是哪里来的呢?因为是常数,所以当c>0我们可以是从源点流出来,流到该点的,所以恒定不变,不参与其它流的流动,所以与源点连边。c<0亦然

对于3,4我们考虑第i个人贡献的区间是[l,r],那么x[i]是在第l个等式出现的,-x[i]是在第r+1个等式出现的,那么当x[i]发生改变时,对这张图的影响只有x[i],-x[i]这两个点,所以将x[i]于-x[i]连一条边,容量是inf,因为第i个人可以选无限多个,距离(即花费)

为cost[i]。y[i]同理,只是距离变成0,因为y[i]是我们设的辅助值,没有实际费用

以下为AC代码:

/**************************************************************

    Problem: 1061

    User: syh0313

    Language: C++

    Result: Accepted

    Time:1892 ms

    Memory:18512 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <string>

#include <cstring>

#include <cmath>

using namespace std;

const long long inf=1000000000000;

const int maxe=40010;

const int maxd=1050;

int n,m,ff[maxe],tt[maxe],to[maxe],nt[maxe],st[maxe],w[maxe];

int pre[maxd],s,t,q[4000010],head,tail,topt;

long long vv[maxe],dis[maxd],inc[maxd],cap[maxe],ans,a[maxd];

bool f[maxd];

void add(int x,int y,long long rong,long long v)

{

    to[topt]=y; nt[topt]=st[x]; st[x]=topt;

    cap[topt]=rong; w[topt]=v; topt++;

    to[topt]=x; nt[topt]=st[y]; st[y]=topt;

    cap[topt]=0; w[topt]=-v; topt++;

}

bool spfa()

{

    for (int i=0;i<=t;i++) dis[i]=inf,f[i]=0,pre[i]=-1;

    dis[s]=0; inc[s]=inf; q[1]=s; f[s]=1; head=tail=1;

    while (head<=tail)

    {

        int x=q[head++]; f[x]=0;

        int p=st[x];

        while (p!=-1)

        {

            if (dis[to[p]]>dis[x]+w[p] && cap[p]>0)

            {

                dis[to[p]]=dis[x]+w[p];

                inc[to[p]]=min(cap[p],inc[x]);

                pre[to[p]]=p;

                if (!f[to[p]]) {f[to[p]]=1; q[++tail]=to[p];}

            }

            p=nt[p];

        }

    }

    if (dis[t]==inf) return 0;

return 1;

}

int main()

{

    memset(st,-1,sizeof st);

    scanf("%d%d",&n,&m);

    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);

    for (int i=1;i<=m;i++) scanf("%d %d %lld",&ff[i],&tt[i],&vv[i]);

    s=n+2; t=n+3;

    for (int i=1;i<=n+1;i++)

     if (a[i]-a[i-1]>=0) add(s,i,a[i]-a[i-1],0);

      else add(i,t,a[i-1]-a[i],0);

    for (int i=1;i<=n;i++) add(i+1,i,inf,0);

    for (int i=1;i<=m;i++) add(ff[i],tt[i]+1,inf,vv[i]);

    while (spfa())

    {

        ans+=dis[t]*inc[t];

        //printf("kkkkk %lld %lld\n",dis[t],inc[t]);

        int se=pre[t];

        while (se!=-1)

        {

            cap[se]-=inc[t];

            cap[se^1]+=inc[t];

            se=pre[to[se^1]];

        }

    }

    printf("%lld\n",ans);

return 0;

}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值