一个神秘的oj2587 你猜是不是dp(线段树优化建图)
哇
这难道不是happiness的翻版题嘛?
从\(S\)向一个点连染成白色的收益
从这个点向\(T\)连染成黑色的收益
对于额外的收益,建一个辅助点,跟区间内的每个点连\(inf\),然后向S/T,连流量为收益
这不就结束了吗?
自信写完,提交
woc!!只有40分?
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 100010;
const int maxm = 2e6+1e2;
const int inf = 2e9;
int point[maxn],nxt[maxm],to[maxm],val[maxm];
int h[maxn],cnt=1;
int n,m;
int a[maxn],b[maxn];
int s,t;
queue<int> q;
int ans;
void addedge(int x,int y,int w)
{
nxt[++cnt]=point[x];
to[cnt]=y;
val[cnt]=w;
point[x]=cnt;
}
void insert(int x,int y,int w)
{
addedge(x,y,w);
addedge(y,x,0);
}
bool bfs(int s)
{
memset(h,-1,sizeof(h));
h[s]=0;
q.push(s);
while (!q.empty())
{
int x = q.front();
q.pop();
for(int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (val[i]>0 && h[p]==-1)
{
h[p]=h[x]+1;
q.push(p);
}
}
}
if (h[t]==-1) return false;
else return true;
}
int dfs(int x,int low)
{
if (x==t || low==0) return low;
int totflow=0;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (val[i]>0 && h[p]==h[x]+1)
{
int tmp = dfs(p,min(low,val[i]));
val[i]-=tmp;
val[i^1]+=tmp;
low-=tmp;
totflow+=tmp;
if (low==0) return totflow;
}
}
if (low>0) h[x]=-1;
return totflow;
}
int dinic()
{
int ans=0;
while (bfs(s))
{
ans=ans+dfs(s,inf);
}
return ans;
}
void build()
{
s=n+m+100;
t=s+1;
for (int i=1;i<=n;i++)
{
if (a[i]>=0 && b[i]>=0)
{
insert(s,i,a[i]);
insert(i,t,b[i]);
}
if (a[i]<0 && b[i]>=0)
{
insert(i,t,abs(a[i])+b[i]);
}
if (a[i]>=0 && b[i]<0)
{
insert(s,i,abs(b[i])+a[i]);
}
if (a[i]<0 && b[i]<0)
{
insert(s,i,abs(b[i]));
insert(i,t,abs(a[i]));
}
if (a[i]>=0) ans+=a[i];
if (b[i]>=0) ans+=b[i];
}
}
int main()
{
freopen("nicai.in","r",stdin);
freopen("nicai.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) b[i]=read();
build();
int num = n+1;
for (int i=1;i<=m;i++)
{
int opt,x,y,z;
opt=read();
x=read();
y=read();
z=read();
if (opt==1)
{
for (int j=x;j<=y;j++)
{
insert(num,j,inf);
}
insert(s,num,z);
num++;
}
else
{
for (int j=x;j<=y;j++)
{
insert(j,num,inf);
}
insert(num,t,z);
num++;
}
ans=ans+z;
}
cout<<ans-dinic()<<endl;
//cout<<dinic()<<endl;
return 0;
}
后来仔细一想。
这么建图的复杂度,简直爆炸呀
不过貌似一段区间同时向一个点连边,这个东西可以优化呀?
哎?好像可以线段树???
这时候就需要我们这个题的重头戏了
线段树优化建图!
线段树优化建图主要是对于一系列一段连续区间向某一个点连边的题。
他的大致思路是
将线段树的节点作为图的点,然后连边的时候,将区间拆成\(log\)个小区间来连边,这样能大大减少边数。然后线段树节点之间的点连边\(inf\),用来确定最小割不会割掉这条边
而一般对于网络流或者双向边的题,一般是需要两颗线段树。
对于这道题,因为是新建的点,需要向\(S/T\)连边
所以需要两颗线段树,但是要注意父亲节点和儿子节点连边的方向
然后对于\(leaf\)节点,我们需要单独记录,并按照上面朴素做法的建图方式建图,然后跑最小割即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
const int maxm = 4e6;
const int inf = 1e9;
int f[8*maxn],g[8*maxn];
int point[maxn],nxt[maxm],to[maxm];
int h[maxn],cnt=1,val[maxm];
int n,m;
int s,t;
int leaf[maxn];
long long ymh=0;
int tmp=1 ;
queue<int> q;
void addedge(int x,int y,int w)
{
nxt[++cnt]=point[x];
to[cnt]=y;
val[cnt]=w;
point[x]=cnt;
}
void insert(int x,int y,int w)
{
addedge(x,y,w);
addedge(y,x,0);
}
void build(int root,int l,int r)
{
if (l==r)
{
leaf[l]=++tmp;
f[root]=tmp;
return;
}
int mid = (l+r) >> 1;
f[root]=++tmp;
build(2*root,l,mid);
build(2*root+1,mid+1,r);
insert(f[root],f[2*root],inf);
insert(f[root],f[2*root+1],inf);
}
void build1(int root,int l,int r)
{
if (l==r)
{
g[root]=leaf[l];
return;
}
int mid = (l+r) >> 1;
g[root]=++tmp;
build1(2*root,l,mid);
build1(2*root+1,mid+1,r);
insert(g[2*root],g[root],inf);
insert(g[2*root+1],g[root],inf);
}
void update(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
insert(p,f[root],inf);
return;
}
int mid =(l+r) >> 1;
if (x<=mid) update(2*root,l,mid,x,y,p);
if (y>mid) update(2*root+1,mid+1,r,x,y,p);
}
void update1(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
insert(g[root],p,inf);
return;
}
int mid =(l+r) >> 1;
if (x<=mid) update1(2*root,l,mid,x,y,p);
if (y>mid) update1(2*root+1,mid+1,r,x,y,p);
}
bool bfs(int s)
{
memset(h,-1,sizeof(h));
h[s]=0;
q.push(s);
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (val[i]>0 && h[p]==-1)
{
h[p]=h[x]+1;
q.push(p);
}
}
}
//cout<<1<<endl;
if (h[t]==-1) return false;
else return true;
}
int dfs(int x,int low)
{
if (x==t || low==0) return low;
int totflow=0;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (val[i]>0 && h[p]==h[x]+1)
{
int tmp = dfs(p,min(low,val[i]));
low-=tmp;
totflow+=tmp;
val[i]-=tmp;
val[i^1]+=tmp;
if (low==0) return totflow;
}
}
if (low>0) h[x]=-1;
return totflow;
}
int dinic()
{
int ans=0;
while (bfs(s))
{
ans=ans+dfs(s,inf);
}
return ans;
}
int b[maxn],w[maxn];
int main()
{
freopen("nicai.in","r",stdin);
freopen("nicai.out","w",stdout);
n=read(),m=read();
build(1,1,n);
build1(1,1,n);
s=maxn-100;
t=s+1;
for (int i=1;i<=n;i++) b[i]=read();
for (int i=1;i<=n;i++) w[i]=read();
for (int i=1;i<=n;i++)
{
if (b[i]>=0) insert(s,leaf[i],b[i]);
else insert(leaf[i],t,-b[i]);
}
for (int i=1;i<=n;i++)
{
if (w[i]>=0) insert(leaf[i],t,w[i]);
else insert(s,leaf[i],-w[i]);
}
for (int i=1;i<=n;i++)
{
if (b[i]>0) ymh=ymh+b[i];
if (w[i]>0) ymh=ymh+w[i];
}
for (int i=1;i<=m;i++)
{
int l,r,opt,x;
opt=read();
l=read();
r=read();
x=read();
++tmp;
if (opt==1)
{
insert(s,tmp,x);
update(1,1,n,l,r,tmp);
}
if (opt==2)
{
insert(tmp,t,x);
update1(1,1,n,l,r,tmp);
}
ymh+=x;
}
//cout<<ymh<<endl;
cout<<ymh-dinic();
return 0;
}
一个神秘的oj2587 你猜是不是dp(线段树优化建图)的更多相关文章
- [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)
[Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...
- BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...
- 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流
[BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...
- 【ARC069F】Flags 2-sat+线段树优化建图+二分
Description 数轴上有 n 个旗子,第 ii 个可以插在坐标 xi或者 yi,最大化两两旗子之间的最小距离. Input 第一行一个整数 N. 接下来 N 行每行两个整数 xi, ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
- 【bzoj4699】树上的最短路(树剖+线段树优化建图)
题意 给你一棵 $n$ 个点 $n-1$ 条边的树,每条边有一个通过时间.此外有 $m$ 个传送条件 $(x_1,y_1,x_2,y_2,c)$,表示从 $x_1$ 到 $x_2$ 的简单路径上的点可 ...
- 【BZOJ4276】[ONTAK2015]Bajtman i Okrągły Robin 线段树优化建图+费用流
[BZOJ4276][ONTAK2015]Bajtman i Okrągły Robin Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2 ...
- 【bzoj3073】[Pa2011]Journeys 线段树优化建图+堆优化Dijkstra
题目描述 Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a, ...
- 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序
题目描述 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r- ...
随机推荐
- redis《三》连接池配置参数
参数 值 setTestWhileIdle() 在空闲时检查有效性 true setMinEvictableIdleTimeMillis() 连接最小空闲时间 1800000L setTimeBetw ...
- 移动端touch事件——单指拖拽
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- vue 基础入门(一)
app-1 :声明式渲染 app-2 :绑定元素特性 v-bind 特性被称为指令.指令带有前缀 v-,以表示它们是 Vue 提供的特殊特性. app-3 app-4 :条件与循环 app-5 ,ap ...
- 状态码1xx-6xx的含义
1xx (临时响应)表示临时响应并需要请求者继续执行操作的状态代码. 100 (继续) 请求者应当继续提出请求. 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分. 101 (切换协议) 请 ...
- 最长回文子序列---DP
问题描述 给定一个字符串s,找到其中最长的回文子序列.可以假设s的最大长度为1000. 解题思路 1.说明 首先要弄清楚回文子串和回文子序列的区别,如果一个字符串是"bbbab", ...
- python 加速运算
原文链接:https://blog.csdn.net/qq_27009517/article/details/103805099 一.加速查找 1.用set而非list import time dat ...
- python实现分水岭算法分割遥感图像
1. 定义 分水岭算法(watershed algorithm)可以将图像中的边缘转化为"山脉",将均匀区域转化为"山谷",在这方面有助于分割目标. 分水岭算法 ...
- VMware安装IPFire防火墙镜像
之后便可以通过WEB登录到管理页面(admin账号,密码是在上面配置的) 详细可参考:https://www.mobibrw.com/2016/4900
- shell脚本中select循环语句用法
shell脚本中select循环语句 1. 脚本中select的语法格式 select VAR in LIST do command1 command2 ... ... commandN done s ...
- JDK 1.7 正式发布,Oracle 官宣免费提供!“新版任你发,我用JDK 8”或成历史?
Oracle公司JDK 17正式发布,JDK 17属于长期支持(LTS)版本,也就是获得8年的技术支持,自2021年9月至2029年9月截止. JDK 17版本更新了很多比较实用的新特性,关于此版本的 ...