1. 程式人生 > >BZOJ5222[Lydsy2017省隊十連測] 怪題

BZOJ5222[Lydsy2017省隊十連測] 怪題

怪題

Description

給出一個長度為nn的整數序列hih_i,現在要通過一些操作將這個序列修改為單調不降序列,即hihi+1h_i ≤ h_i+1

可以用的操作有mm種,第ii種操作可以通過支付cic_i的代價將一段長度恰為lil_i的連續子序列+1+11−1(由對應的操作符確定是+1+1還是1−1,具體參考輸入格式)。

不限制每種操作的使用次數,序列中的hih_i可以被改為任意整數(可以是負數),求最小代價,無解輸出1−1

Input

第一行,兩個整數n,mn,m

第二行,nn個整數hih_i

接下來mm行,每行格式為opi,li,ciop_i,l_i,c_i空格隔開,其中opiop_i為一個字元,表示這種操作是+1+1還是1−1

Output

輸出一行一個整數表示最小代價,若無解輸出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\%的資料,n,m5hi10ci3n,m≤5,h_i≤10,c_i≤3

對於另20%20\%的資料,li=1hi500l_i=1,h_i≤500

對於100%100\%的資料,n,m200lin1hi,ci106n,m≤200,l_i≤n,1≤h_i,c_i≤10^6

題解

我們的目標是讓序列單增,所以我們並不關心具體的數值,只需讓差分陣列全大於00即可。

果斷差分,發現一次區間操作會把一個數1-1,一個數+1+1,可以看做一單位流量的轉移,那麼我們就可以愉快的建圖了:

源點先向一頭一尾連條infinf邊,然後再遍歷中間的點,源點向差分為正的點連邊,差分為負的點向匯點連邊;對於每個操作連O

(n)O(n)條邊,單位費用為cic_i

接下來就可以歡樂費用流了,不滿流輸出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();}