【BZOJ 5222】[Lydsy2017省队十连测]怪题
题目大意:
给一个长度为$n(n<=200)$的数列$h$,再给$m$个可以无限使用的操作,第$i$个操作为给长度为花费$c_i$的价值给长度为$l_i$的数列子序列+1或-1,求将数列变为不下降数列的最小花费。
题解:
第一部分(上下界最小费用可行流):
设$h_0=-inf,h_{n+1}=inf$,令$a$为$h$的差分数组,即$a_i=h_{i}-h_{i-1}$。考虑当对于区间$[l,r]$操作时(比如+1),相当于$a_{r+1}$减少1,$a_{l}$增加1。若将$a$数组看做点集,这个变化相当于从$r+1$到$l$的一条流量为$1$的有向边,反之(-1)亦然。
显然问题相当于把$a$数组元素均变为 不为0。那么我们由S向$a_{i}>0$的位置连$flow=[0,a_{i}],cost=0$的边,表示${i}$可以减少流量上下界,对于$a_{i}<0$的位置,我们至少要使其增加$-a_i$所以我们向$T$连$flow=[-a_i,inf],cost=0$的边。对于每个操作我们由于可无限使用我们就给所有合法位置连$flow=[0,inf],cost=c_{i}$的边,然后我们可以跑一个上下界解决问题。
等等,这样的确解决了问题,不过我们观察一下这个图,会发现上下界源点只连向了$T$,而上下界汇点只被那些$a_{i}<0$的点连接到。
我们把这个图转化一下,会发现对于上面的建图方式,我们只把$a_{i}<0$的边建成$flow=-a_i,cost=0$的边即可,根本不需要跑上下界。这就是另外一种思考方式。
第二部分(最小费用最大流):
我们考虑那些$a_{i}<0$的点变为$0$一定比使其变为任一整数更优秀,同时这也是我们的判断有没有解的依据。
所以我们就直接由$a_{i}<0$的点连向$T$的边为$flow=-a_i,cost=0$即可,如果所有连向$T$的边都流满,说明有解,同时由于上述性质,一定是最优解。
不过这两个时间复杂度并没有太大区别。
代码:
#include "bits/stdc++.h" using namespace std; #define inf 0x3f3f3f3f inline int read() {
int s=,k=;char ch=getchar ();
while (ch<''|ch>'') ch=='-'?k=-:,ch=getchar();
while (ch>&ch<='') s=s*+(ch^),ch=getchar();
return s*k;
} const int N=1e3+; struct edges {
int v,cap,cost;edges *pair,*last;
}edge[N*N],*head[N];int cnt; inline void push(int u,int v,int cap,int cost) {
edge[++cnt]=(edges){v,cap,cost,edge+cnt+,head[u]},head[u]=edge+cnt;
edge[++cnt]=(edges){u,,-cost,edge+cnt-,head[v]},head[v]=edge+cnt;
} int S,T,ss,tt,n,fl,m;
int piS,vis[N];
long long cost; inline int aug(int x,int w) {
if (x==T) return cost+=1ll*piS*w,fl+=w,w;
vis[x]=true;
int ret=;
for (edges *i=head[x];i;i=i->last)
if (i->cap&&!i->cost&&!vis[i->v]) {
int flow=aug(i->v,min(i->cap,w));
i->cap-=flow,i->pair->cap+=flow,ret+=flow,w-=flow;
if (!w) break;
}
return ret;
} inline bool modlabel() {
static int d[N];
memset(d,0x3f,sizeof d);d[T]=;
static deque<int> q;q.push_back(T);
int dt;
while (!q.empty()) {
int x=q.front();q.pop_front();
for (edges *i=head[x];i;i=i->last)
if (i->pair->cap&&(dt=d[x]-i->cost)<d[i->v])
(d[i->v]=dt)<=d[q.size()?q.front():]
?q.push_front(i->v):q.push_back(i->v);
}
for (int i=S;i<=T;++i)
for (edges *j=head[i];j;j=j->last)
j->cost+=d[j->v]-d[i];
piS+=d[S];
return d[S]<inf;
} inline void solve() {
piS = cost = ;
while(modlabel())
do memset(vis,,sizeof vis);
while(aug(S, inf));
} int h[N],a[N],c[N],l[N],typ[N];
int f[N],g[N]; int main(){
n=read(),m=read();
for (int i=;i<=n;++i)
h[i]=read();
for (int i=n;i>;--i)
h[i]=h[i]-h[i-];
h[]=inf,h[n+]=inf;
ss=n+,tt=ss+,T=tt+;
char opt[];
for (int i=;i<=m;++i) {
scanf("%s",opt),l[i]=read(),c[i]=read();
typ[i]=opt[]=='+';
}
++n;
for (int i=;i<=n;++i)
if (h[i] > )
push(ss,i,h[i],);
else if(h[i]<) push(i,tt,inf,),a[i]=-h[i],a[tt]+=h[i],push(i,T,a[i],);
push(tt,ss,inf,);
push(S,tt,-a[tt],);
memset(f,0x3f,sizeof(f));
f[]=;
memcpy(g,f,sizeof g);
for (int j=;j<=m;++j)
for (int k=l[j];k<=n;k+=l[j])
if(typ[j])
for (int i=n-;i>=l[j];--i)
f[i]=min(f[i],f[i-l[j]]+c[j]);
else
for (int i=n-;i>=l[j];--i)
g[i]=min(g[i],g[i-l[j]]+c[j]);
// puts()
for (int j=;j<=m;++j)
if (typ[j]){
if (f[l[j]]==c[j])
for (int i=l[j]+;i<=n;++i)
push(i,i-l[j],inf,c[j]);
}else
if (g[l[j]]==c[j])
for (int i=;i+l[j]<=n;++i)
push(i,i+l[j],inf,c[j]);
solve();
if (fl==-a[tt])
printf("%lld\n",cost);
else puts("-1");
}
上下界
#include "bits/stdc++.h" using namespace std; #define inf 0x3f3f3f3f inline int read() {
int s=,k=;char ch=getchar ();
while (ch<''|ch>'') ch=='-'?k=-:,ch=getchar();
while (ch>&ch<='') s=s*+(ch^),ch=getchar();
return s*k;
} const int N=1e3+; struct edges {
int v,cap,cost;edges *pair,*last;
}edge[N*N],*head[N];int cnt; inline void push(int u,int v,int cap,int cost) {
edge[++cnt]=(edges){v,cap,cost,edge+cnt+,head[u]},head[u]=edge+cnt;
edge[++cnt]=(edges){u,,-cost,edge+cnt-,head[v]},head[v]=edge+cnt;
} int S,T,ss,tt,n,fl,m;
int piS,vis[N];
long long cost; inline int aug(int x,int w) {
if (x==T) return cost+=1ll*piS*w,fl+=w,w;
vis[x]=true;
int ret=;
for (edges *i=head[x];i;i=i->last)
if (i->cap&&!i->cost&&!vis[i->v]) {
int flow=aug(i->v,min(i->cap,w));
i->cap-=flow,i->pair->cap+=flow,ret+=flow,w-=flow;
if (!w) break;
}
return ret;
} inline bool modlabel() {
static int d[N];
memset(d,0x3f,sizeof d);d[T]=;
static deque<int> q;q.push_back(T);
int dt;
while (!q.empty()) {
int x=q.front();q.pop_front();
for (edges *i=head[x];i;i=i->last)
if (i->pair->cap&&(dt=d[x]-i->cost)<d[i->v])
(d[i->v]=dt)<=d[q.size()?q.front():]
?q.push_front(i->v):q.push_back(i->v);
}
for (int i=S;i<=T;++i)
for (edges *j=head[i];j;j=j->last)
j->cost+=d[j->v]-d[i];
piS+=d[S];
return d[S]<inf;
} inline void solve() {
piS = cost = ;
while(modlabel())
do memset(vis,,sizeof vis);
while(aug(S, inf));
} int h[N],a[N],c[N],l[N],typ[N];
int f[N],g[N]; int main(){
n=read(),m=read();
for (int i=;i<=n;++i)
h[i]=read();
for (int i=n;i>;--i)
h[i]=h[i]-h[i-];
h[]=inf,h[n+]=inf;
// ss=n+2,tt=ss+1,T=tt+1;
char opt[];
for (int i=;i<=m;++i) {
scanf("%s",opt),l[i]=read(),c[i]=read();
typ[i]=opt[]=='+';
}
++n;T=n+;
for (int i=;i<=n;++i)
if (h[i] > )
push(S,i,h[i],);
else if(h[i]<) push(i,T,-h[i],),a[tt]+=h[i];//,push(i,T,a[i],0);
// push(tt,ss,inf,0);
// push(S,tt,-a[tt],0);
memset(f,0x3f,sizeof(f));
f[]=;
memcpy(g,f,sizeof g);
for (int j=;j<=m;++j)
for (int k=l[j];k<=n;k+=l[j])
if(typ[j])
for (int i=n-;i>=l[j];--i)
f[i]=min(f[i],f[i-l[j]]+c[j]);
else
for (int i=n-;i>=l[j];--i)
g[i]=min(g[i],g[i-l[j]]+c[j]);
for (int j=;j<=m;++j)
if (typ[j]){
if (f[l[j]]==c[j])
for (int i=l[j]+;i<=n;++i)
push(i,i-l[j],inf,c[j]);
}else
if (g[l[j]]==c[j])
for (int i=;i+l[j]<=n;++i)
push(i,i+l[j],inf,c[j]);
solve();
// printf("%d %d\n",fl);
if (fl==-a[tt])
printf("%lld\n",cost);
else puts("-1");
}
最大流
【BZOJ 5222】[Lydsy2017省队十连测]怪题的更多相关文章
- bzoj 5216 [Lydsy2017省队十连测]公路建设 线段树维护 最小生成树
[Lydsy2017省队十连测]公路建设 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 93 Solved: 53[Submit][Status][ ...
- bzoj 5216: [Lydsy2017省队十连测]公路建设
5216: [Lydsy2017省队十连测]公路建设 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 66 Solved: 37[Submit][St ...
- bzoj 5218: [Lydsy2017省队十连测]友好城市
题意: 这题显然直接tarjan是做不了的. 这里安利另一个求SCC的算法Kosaraju,学习的话可以见这篇博客 于是结合莫队,我们有了个暴力. 发现主要瓶颈是dfs过程中找最小的未经过的点,我们用 ...
- bzoj 5217: [Lydsy2017省队十连测]航海舰队
Description Byteasar 组建了一支舰队!他们现在正在海洋上航行着.海洋可以抽象成一张n×m 的网格图,其中有些位置是" .",表示这一格是海水,可以通过:有些位置 ...
- BZOJ 5215: [Lydsy2017省队十连测]商店购物
裸题 注意+特判 #include<cstdio> using namespace std; const int mod=1e9+7; int F[1000005],mi[10000005 ...
- @bzoj - 5219@ [Lydsy2017省队十连测]最长路径
目录 @description@ @solution@ @accepted code@ @details@ @description@ 在Byteland一共有n个城市,编号依次为1到n,形成一个n个 ...
- BZOJ5243 : [Lydsy2017省队十连测]绝版题
要找的就是这棵树的带权重心,以带权重心为根时每棵子树的权值和不超过总权值和的一半. 因此按$\frac{v[i]}{\sum v[i]}$的概率随机选取一个点$x$,则重心有$\frac{1}{2}$ ...
- Lydsy2017省队十连测
5215: [Lydsy2017省队十连测]商店购物 可能FFT学傻了,第一反应是前面300*300背包,后面FFT... 实际上前面背包,后面组合数即可.只是这是一道卡常题,需要注意常数.. //A ...
- 2018.09.26 bzoj5218: [Lydsy2017省队十连测]友好城市(回滚莫队)
传送门 比较简单的一道回滚莫队吧. 每次询问用bitset优化kosaraju统计答案. 就是有点难调. 然后向dzyo学长学习了回滚莫队的一种简洁的实现方式,就是直接建立一个sqrt(m)∗sqrt ...
随机推荐
- 大数据小视角1:从行存储到RCFile
前段时间一直在忙碌写毕设与项目的事情,很久没有写一些学习心得与工作记录了,开了一个新的坑,希望能继续坚持写作与记录分布式存储相关的知识.为什么叫小视角呢?因为属于随想型的内容,可能一个由小的视角来审视 ...
- 简单工厂,Factory Method(工厂方法)和Abstract Factory(抽象工厂)模式
对于简单工厂来说,它的工厂只能是这个样子的 public class SimplyFactory { /** * 静态工厂方法 */ public static Prouct factory(Str ...
- 自制无线共享工具C++源代码
// wire.cpp : 定义控制台应用程序的入口点. // #include <iostream> #include <string.h> using namespace ...
- Django升级1.8的一些问题
1.最明显的问题当然是Settings设置中关于模板的设置数据结构发生变化,这个就不细说了,你开个Django的1.8的新项目就知道怎么改了 2.migrations问题,这个问题是1.8最主要的修改 ...
- 微信公众号网页授权登录--JAVA
网上搜资料时,网友都说官方文档太垃圾了不易看懂,如何如何的.现在个人整理了一个通俗易懂易上手的,希望可以帮助到刚接触微信接口的你. 请看流程图!看懂图,就懂了一半了: 其实整体流程大体只需三步:用户点 ...
- 单调队列——求m区间内的最小值
单调队列,顾名思义是指队列内的元素是有序的,队头为当前的最大值(单调递减队列)或最小值(单调递增序列),以单调递减队列为例来看队列的入队和出队操作: 1.入队: 如果当前元素要进队,把当前元素和队尾元 ...
- oracle 游标简单示例
1.游标的概念以及作用 游标(Cursor)可以使用户想操作数组一样对查询出来的结果集进行操作,可以形象的看做一个变动的光标,其实际行是一个指针,它在一段Oracle存放数据查询结果集或数据 操作集的 ...
- python+selenium 环境搭建以及元素定位
在给公司同事给培训了WEB自动化框架,现在和大家分享交流下
- 回忆一下我的运维时期 关于Impact的架构服务器集群
Impact EDMP平台 Email Direct Marketing Platfrom 电子邮件营销平台 EDM 是 Email Direct Marketing 的缩写,即电子邮件营销,简 ...
- 如何使用 toml 配置 SpaceVim
配置 SpaceVim 主要包括以下几个内容: 设置 SpaceVim 选项 启动/禁用模块 添加自定义插件 添加自定义按键映射以及插件配置 设置SpaceVim选项 原先,在 init.vim 文件 ...