10.8考试爆炸记

前言

30分垫底,我太菜了,
只提供一道怪题

题面

给出一个长度为 n 的整数序列hi,现在要通过一些操作将这个序列修改为单调不降序列,即 hihi+1
可以用的操作有m种,第 i 种操作可以通过支付 ci 的代价将一段长度恰为 li 的连续子序列 +1 1 (由对应的操作符确定是 +1 还是 1 ,具体参考输入格式)。
不限制每种操作的使用次数,序列中的 hi 可以被改为任意整数(可以是负数),求最小代价,无解输出 1

题解

考虑到区间加减可以被视为对两点的操作。
同时对序列作差,然后、、、然后就是一个费用流了、、、
可以将作差为负的点向汇点建费用为0,流量为差值的绝对值的边,源点向作差为正的点建费用为0,流量为差值绝对值的边。
对于总共 m 组操作,我们先去除效果相同费用较大的,再进行以下处理:
(1)如果第i个操作为 +1 ,则从较后的点向较前的点连流量为 ,费用为 c[i] 的边
(2)如果第 i 个操作为1,则从较前的点向较后的点连流量为 ,费用为 c[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值