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,保证存在至少一种排列。
题解
先考虑没有任何限制的时候,
一定就是将所有正数放在一起,而负数就放在一边。
可惜题目给出了一些限制,
为了满足这些限制,可以采取以下这几种方式来调整顺序,
把某个正数从答案区间的前面扔出,把某个正数从答案区间的后面扔出,
或者将某个负数增加到答案序列。
也许很难想到是网络流,
但想到了,连边就对于这上面的三种情况。
将每个位置拆成两个点
从超级源S连向所有的正数,边权为整个数的数值,
这一部分边如果被割掉就表示这个正数被从前面扔出。
所有正数的另外一个点,连向超级汇T,边权为这个数的数值,
这一部分边如果被割掉就表示这个正数被从后面扔出。
对于所有的负数,
自己的两个点之间连一条边,边权为它的绝对值。
这一部分的边如果被割掉就表示某个负数加入了答案。
现在就是要看限制条件了,
对于一个限制条件:a,b
就分别从a的两个点连向b的两个点,边权为正无穷,因为这些边是不能被割掉的。
然后求一下最最大流就行了。
code
#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 10003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
using namespace std;
char ch;
void read(int &n)
{
n=0;
ch=G();
while((ch<'0' || ch>'9') && ch!='-')ch=G();
ll w=1;
if(ch=='-')w=-1,ch=G();
while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
n*=w;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}
int nxt[N*2],to[N*2],v[N*2],lst[N],cur[N],tot;
int q[N],h[N],S,T,ans,n,m,x,y;
bool bfs()
{
int head=0,tail=1;
for(int i=0;i<=T;i++)h[i]=-1;
q[0]=S;h[S]=0;
while(head!=tail)
{
int now=q[head];head++;
for(int i=lst[now];i;i=nxt[i])
if(v[i] && h[to[i]]==-1)
{
h[to[i]]=h[now]+1;
q[tail++]=to[i];
}
}
return h[T]!=-1;
}
int dfs(int x,int f)
{
if(x==T)return f;
int w,used=0;
for(int i=cur[x];i;i=nxt[i])
if(h[to[i]]==h[x]+1)
{
w=f-used;
w=dfs(to[i],min(w,v[i]));
v[i]-=w;v[i^1]+=w;
if(v[i])cur[x]=i;
used+=w;
if(used==f)return f;
}
if(!used)h[x]=-1;
return used;
}
void dinic()
{
while(bfs())
{
for(int i=0;i<=T;i++)
cur[i]=lst[i];
ans-=dfs(S,inf);
}
}
void ins(int x,int y,int z)
{
nxt[++tot]=lst[x];
to[tot]=y;
v[tot]=z;
lst[x]=tot;
}
void ins_(int x,int y,int z)
{
ins(x,y,z);
ins(y,x,0);
}
int main()
{
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
tot=1;read(n);read(m);
S=n*2+1;T=S+1;
for(int i=1;i<=n;i++)
{
read(x);
if(x>0)ins_(S,i,x),ins_(i+n,T,x),ans+=x;else ins_(i,i+n,-x);
}
for(int i=1;i<=m;i++)
read(x),read(y),ins_(x,y,inf),ins_(x+n,y+n,inf);
dinic();
write(ans);
}