前言
30分垫底,我太菜了,耶。
只提供一道怪题
题面
给出一个长度为
n
的整数序列
可以用的操作有m种,第 i 种操作可以通过支付 ci 的代价将一段长度恰为 li 的连续子序列
+1
或
−1
(由对应的操作符确定是
+1
还是
−1
,具体参考输入格式)。
不限制每种操作的使用次数,序列中的
hi
可以被改为任意整数(可以是负数),求最小代价,无解输出
−1
。
题解
考虑到区间加减可以被视为对两点的操作。
同时对序列作差,然后、、、然后就是一个费用流了、、、
可以将作差为负的点向汇点建费用为0,流量为差值的绝对值的边,源点向作差为正的点建费用为0,流量为差值绝对值的边。
对于总共
m
组操作,我们先去除效果相同费用较大的,再进行以下处理:
(1)如果第
(2)如果第
i
个操作为
同时我们要注意到第一个点和最后一个点无论如何加减是不会与第0个点抑或是最后一个点的后一个点产生冲突的。
两边特殊赋值就完了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#define int long long
using namespace std;
inline int read(){
int i=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch);ch=getchar())
if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar())
i=(i<<3)+(i<<1)+(ch^48);
return i*f;
}
int buf[1024];
inline void write(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
#define stan 333
#define sten 555555
#define stin 11
int tot,nxt[sten],ret,first[stan],goal[sten],wide[sten],length[sten],to[stan];
bool real[stan],exi[stan];
char ord[stin];
int S,T,t,n,m,h[stan],ans,cheque;
struct option{
int k,l,c;
}opt[sten];
bool cmp(option a,option b){
if(a.k!=b.k) return a.k<b.k;
else if(a.l!=b.l) return a.l<b.l;
else return a.c<b.c;
}
bool check(int a,int b){
return opt[a].k!=opt[b].k||opt[a].l!=opt[b].l;
}
void addedge(int a,int b,int c,int d){
++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;wide[tot]=c;length[tot]=d;
++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;wide[tot]=0;length[tot]=-d;
return ;
}
bool spfa(){
static int que[3333];
int q=1;
for(int i=S;i<=T;++i)
to[i]=999999999,exi[i]=false;
to[S]=0;que[q]=S;
for(int i=1;i<=q;++i){
int u=que[i];real[u]=false;
for(int p=first[u];p;p=nxt[p])
if(wide[p]&&to[goal[p]]>to[u]+length[p]){
to[goal[p]]=to[u]+length[p];
if(!real[goal[p]]){
real[goal[p]]=true;
que[++q]=goal[p];
}
}
}
return to[T]!=999999999;
}
int dfs(int pos,int flow){
if(pos==T){
ret+=flow*to[T];
return flow;
}
int ret=0,data;
exi[pos]=true;
for(int p=first[pos];p;p=nxt[p])
if(!exi[goal[p]]&&to[goal[p]]==to[pos]+length[p]&&wide[p]){
data=dfs(goal[p],min(flow-ret,wide[p]));
if(data){
wide[p]-=data;
wide[p^1]+=data;
ret+=data;
if(ret==flow) return flow;
}
}
return ret;
}
int solve(){
ret=0;
while(spfa()) dfs(S,999999999);
return ret;
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;++i)
h[i]=read();
for(int i=n;i>=1;--i)
h[i]-=h[i-1];
h[1]=h[++n]=999999999;
for(int i=1;i<=m;++i){
scanf("%s",ord);
opt[i].k=ord[0]=='-'?-1:1;
opt[i].l=read();
opt[i].c=read();
}
sort(opt+1,opt+m+1,cmp);
for(int i=1;i<=m;++i)
if(check(t,i))
opt[++t]=opt[i];
m=t;
S=0;T=n+1;
tot=1;
for(int i=1;i<=n;++i)
if(h[i]<0) addedge(i,T,-h[i],0);
cheque=tot;
for(int i=1;i<=n;++i)
if(h[i]>0) addedge(S,i,h[i],0);
for(int i=1;i<=m;++i)
if(opt[i].k==1)
for(int j=1;j<=n-opt[i].l;++j)
addedge(j+opt[i].l,j,999999999,opt[i].c);
else
for(int j=1;j<=n-opt[i].l;++j)
addedge(j,j+opt[i].l,999999999,opt[i].c);
ans=solve();
for(int i=2;i<=cheque;i+=2)
if(wide[i]){
puts("-1");
return 0;
}
write(ans);
return 0;
}