原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5222
怪题
Description
给出一个长度为 n n n的整数序列 h i h_i hi,现在要通过一些操作将这个序列修改为单调不降序列,即 h i ≤ h i + 1 h_i ≤ h_i+1 hi≤hi+1。
可以用的操作有 m m m种,第 i i i种操作可以通过支付 c i c_i ci的代价将一段长度恰为 l i l_i li的连续子序列 + 1 +1 +1或 − 1 −1 −1(由对应的操作符确定是 + 1 +1 +1还是 − 1 −1 −1,具体参考输入格式)。
不限制每种操作的使用次数,序列中的 h i h_i hi可以被改为任意整数(可以是负数),求最小代价,无解输出 − 1 −1 −1。
Input
第一行,两个整数 n , m n,m n,m。
第二行, n n n个整数 h i h_i hi。
接下来 m m m行,每行格式为 o p i , l i , c i op_i,l_i,c_i opi,li,ci空格隔开,其中 o p i op_i opi为一个字符,表示这种操作是 + 1 +1 +1还是 − 1 −1 −1。
Output
输出一行一个整数表示最小代价,若无解输出 − 1 −1 −1。
Sample Input
10 10
23 1 8 14 2 3 15 50 53 53
+ 4 6
- 1 10
+ 2 4
+ 4 2
- 3 5
+ 1 2
+ 3 2
+ 5 7
- 1 6
+ 4 5
Sample Output
96
HINT
对于 20 % 20\% 20%的数据, n , m ≤ 5 , h i ≤ 10 , c i ≤ 3 n,m≤5,h_i≤10,c_i≤3 n,m≤5,hi≤10,ci≤3。
对于另 20 % 20\% 20%的数据, l i = 1 , h i ≤ 500 l_i=1,h_i≤500 li=1,hi≤500。
对于 100 % 100\% 100%的数据, n , m ≤ 200 , l i ≤ n , 1 ≤ h i , c i ≤ 1 0 6 n,m≤200,l_i≤n,1≤h_i,c_i≤10^6 n,m≤200,li≤n,1≤hi,ci≤106。
题解
我们的目标是让序列单增,所以我们并不关心具体的数值,只需让差分数组全大于 0 0 0即可。
果断差分,发现一次区间操作会把一个数 − 1 -1 −1,一个数 + 1 +1 +1,可以看做一单位流量的转移,那么我们就可以愉快的建图了:
源点先向一头一尾连条 i n f inf inf边,然后再遍历中间的点,源点向差分为正的点连边,差分为负的点向汇点连边;对于每个操作连 O ( n ) O(n) O(n)条边,单位费用为 c i c_i ci。
接下来就可以欢乐费用流了,不满流输出 − 1 -1 −1。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=5e3+5,inf=0x3f3f3f3f;
struct sd{int to,fl,val;}ed[M*20];
int n,m,start,end,id,dis[M],minf[M],pre[M],que[M];
long long ans1,ans2,full;
bool vis[M];
char ch[2];
vector<int>mmp[M];
queue<int>dui;
void add(int u,int v,int w,int f)
{
mmp[u].push_back(id);ed[id++]=(sd){v,w,f};
mmp[v].push_back(id);ed[id++]=(sd){u,0,-f};
}
bool spfa(int s,int t)
{
fill(dis,dis+3+n,inf);
memset(vis,0,sizeof(vis));
dui.push(s);dis[s]=0;vis[s]=1;minf[s]=inf;
int f,to,fl,hh,val;
while(!dui.empty())
{
f=dui.front();dui.pop();
vis[f]=0;
for(int i=mmp[f].size()-1;i>=0;--i)
{
hh=mmp[f][i];to=ed[hh].to;fl=ed[hh].fl;val=ed[hh].val;
if(fl>0&&dis[to]>dis[f]+val)
{
dis[to]=dis[f]+val;
minf[to]=min(minf[f],fl);
pre[to]=hh;
if(!vis[to])dui.push(to),vis[to]=1;
}
}
}
return dis[t]!=inf;
}
void up(int s,int t)
{
int v=t,hh;
while(v!=s)
{
hh=pre[v];
ed[hh].fl-=minf[t];
ed[hh^1].fl+=minf[t];
v=ed[hh^1].to;
}
ans1+=minf[t];
ans2+=1ll*minf[t]*dis[t];
}
void in()
{
scanf("%d%d",&n,&m);
start=n+1,end=n+2;
for(int i=1;i<=n;++i)scanf("%d",&que[i]);
for(int i=0;i<=n;++i)que[i]=que[i+1]-que[i];
add(start,n,inf,0),add(start,0,inf,0);
for(int i=1;i<n;++i)que[i]>0?add(start,i,que[i],0):(full-=que[i],add(i,end,-que[i],0));
for(int i=1,a,b,l,c,j;i<=m;++i)
{
scanf("%s%d%d",ch,&l,&c);
for(a=0,b=l;b<=n;++a,++b)ch[0]=='+'?add(b,a,inf,c):add(a,b,inf,c);
}
}
void ac()
{
while(spfa(start,end))up(start,end);
printf("%lld\n",ans1==full?ans2:-1);
}
main(){in();ac();}