BZOJ5222[Lydsy2017省隊十連測] 怪題
阿新 • • 發佈:2018-12-12
怪題
Description
給出一個長度為的整數序列,現在要通過一些操作將這個序列修改為單調不降序列,即。
可以用的操作有種,第種操作可以通過支付的代價將一段長度恰為的連續子序列或(由對應的操作符確定是還是,具體參考輸入格式)。
不限制每種操作的使用次數,序列中的可以被改為任意整數(可以是負數),求最小代價,無解輸出。
Input
第一行,兩個整數。
第二行,個整數。
接下來行,每行格式為空格隔開,其中為一個字元,表示這種操作是還是。
Output
輸出一行一個整數表示最小代價,若無解輸出。
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
對於的資料,。
對於另的資料,。
對於的資料,。
題解
我們的目標是讓序列單增,所以我們並不關心具體的數值,只需讓差分陣列全大於即可。
果斷差分,發現一次區間操作會把一個數,一個數,可以看做一單位流量的轉移,那麼我們就可以愉快的建圖了:
源點先向一頭一尾連條邊,然後再遍歷中間的點,源點向差分為正的點連邊,差分為負的點向匯點連邊;對於每個操作連條邊,單位費用為。
接下來就可以歡樂費用流了,不滿流輸出。
程式碼
#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();}