JZOJ 5669. 【GDSOI2018模拟4.19】排列

9 篇文章 0 订阅
6 篇文章 0 订阅

Description

有 n 个数 x1 ~xn 。你需要找出它们的一个排列,满足 m 个条件,每个条件形如 x_a 必须在x_b之前。在此基础上,你要最大化这个排列的最大子段和。

Input

第一行两个整数 n,m,第二行 n 个整数 x1 ~xn ,接下来 m 行每行两个整数 a,b。

Output

输出一行一个整数表示最大子段和。

Sample Input

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

Sample Output

6

Data Constraint

Subtask 1 (5pts):n<=10。
Subtask 2 (20pts):n<=20。
Subtask 3 (19pts):m=n-1 且 x1 一定在排列的第一位。
Subtask 4 (56pts):无特殊限制。 对于全部数据,n<=500,m<=1000,|x i |<=1000,保证存在至少一种排列。

Solution

  • 先贴题解:

Solution

  • 看到数据范围,就应该想到网络流!!!

  • 先把权值为正的数放到一起,但全部选就意味着同时也要选很多负的,这样不一定最优。

  • 考虑把其中一些点扔走(往左右扔),或者干脆把中间的负点也选了算了。

  • 于是向上面说的那样连边跑最小割,用正权值和-最小割即为答案。

Code

#include<cstdio>
#include<cctype>
using namespace std;
const int N=1005,inf=1e9;
int s,t,ans,tot=1;
int first[N],nex[N*6],en[N*6],w[N*6];
int dis[N],gap[N*6],cur[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<1)+(X<<3)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void ins(int x,int y,int z)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline void insert(int x,int y,int z)
{
    ins(x,y,z),ins(y,x,0);
}
int sap(int x,int y)
{
    if(x==t) return y;
    int use=0;
    for(int i=first[x];i;i=nex[i])
        if(w[i] && dis[x]==dis[en[i]]+1)
        {
            int sum=sap(en[i],min(y-use,w[i]));
            w[i]-=sum,w[i^1]+=sum,use+=sum;
            if(dis[s]>t || use==y) return use;
        }
    cur[x]=first[x];
    if(!--gap[dis[x]]) dis[s]=t+1;
    gap[++dis[x]]++;
    return use;
}
int main()
{
    freopen("permutation.in","r",stdin);
    freopen("permutation.out","w",stdout);
    int n=read(),m=read();
    s=n<<1|1,t=s+1;
    for(int i=1;i<=n;i++)
    {
        int x=read();
        if(x>0)
        {
            ans+=x;
            insert(s,i,x);
            insert(i+n,t,x);
        }else insert(i,i+n,-x);
    }
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        insert(x,y,inf);
        insert(x+n,y+n,inf);
    }
    for(int i=1;i<=t;i++) cur[i]=first[i];
    gap[0]=t;
    while(dis[s]<=t) ans-=sap(s,inf);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值