题目
题目链接
UOJ#77
BZOJ3218
题目描述
分析
感谢VFleaKing的博客中的讲解和geng4512的博客中的代码,涨了不少知识。
网络流的做法
首先我们来看看答案是怎么得到的
ans=∑ii是白色wi+∑ii是黑色bi−∑ii很奇怪pi
我们转化一下这个等式
ans=∑i=1nwi+bi−∑ii是白色bi−∑ii是黑色wi−∑ii很奇怪pi
求出最小的 ∑i是白色ibi+∑i是黑色iwi+∑i很奇怪ipi即可
可以考虑最小割。
怎么建图呢?
从 S到 i连一条边,容量为 bi,从 i到 T连一条边,容量为 wi。
若 i属于 S,则选择的黑色,否则选择白色。
然后考虑奇怪的i。
构造一个 i′,使存在 {j|1<=j<i,li<=aj<=ri且 j为白色 }时 i′在 T
,若 i′在 T, i在 S那么就会付出 pi的代价。
我们从 i向 i′连一条容量为 pi的边,从 i′到 {j|1<=j<i,li<=aj<=ri}都连一条边,跑网络流。
求出最大流flow。
ans=∑i=1nwi+bi−flow
但是由于边十分多,显然会Memory Limit Exceeded and Time Limit Exceeded
考虑优化
用可持久化线段树优化网络流
假设ai互不相等:
我们以ai为下标
如果不考虑1<=j<i,i就会和一个区间内的所有点连边,似乎和线段树有关系。
我们直接把边连在区间上。
在线段树中,父亲向儿子连一条容量+∞的边,然后叶子节点在连会相应的i即可。
考虑1<=j<i,我们将线段树可持久化,然后第i个点向第i-1个版本的线段树连边即可。
当有存在的{k|k<=i,ak=ai}的k时,我们这样连边之后只连接了i,没有连接k,怎么解决这个问题呢?
我们将每个叶子节点向它前面的一个版本的叶子节点连边即可。
代码
#include
#include
#include
#include
#define MAXN 5000
#define INF 0x7fffffff
#define MAXLOG 13
using namespace std;
queue<int>q;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
struct node{
int cap,v;
node *next,*back;
}*adj[MAXN*MAXLOG+10],edge[MAXN*100+10],*ecnt=edge;
int n,a[MAXN+10],b[MAXN+10],w[MAXN+10],l[MAXN+10],r[MAXN+10],p[MAXN+10],d[MAXN*3+10],m,S,T,ans,tot,dist[MAXN*MAXLOG+MAXN+10],flow,vd[MAXN*MAXLOG+MAXN+10];
struct Seg_tree{
Seg_tree *ch[2];
}tree[MAXN*MAXLOG+10],*tcnt=tree,*root[MAXN+10],*nil=tree;
void addedge(int u,int v,int cap){
node *p=++ecnt;
p->v=v;
p->cap=cap;
p->next=adj[u];
adj[u]=p;
p=p->back=++ecnt;
p->v=u;
p->cap=0;
p->next=adj[v];
adj[v]=p;
}
void insert(Seg_tree *&x,Seg_tree *&y,int l,int r,int i){
x=++tcnt;
*x=*y;
if(l==r){
addedge(T+x-nil,i,INF);
if(y!=nil)
addedge(T+x-nil,T+y-nil,INF); //处理相同的a[i]
return;
}
int mid=(l+r)>>1;
if(a[i]>mid)
insert(x->ch[1],y->ch[1],mid+1,r,i);
else
insert(x->ch[0],y->ch[0],l,mid,i);
if(x->ch[1])
addedge(T+x-nil,T+x->ch[1]-nil,INF);
if(x->ch[0])
addedge(T+x-nil,T+x->ch[0]-nil,INF);
}
void link(Seg_tree *x,int ll,int rr,int i){
if(ll>r[i]||l[i]>rr)
return;
if(l[i]<=ll&&r[i]>=rr){
addedge(n+i,T+x-nil,INF);
return;
}
int mid=(ll+rr)>>1;
if(x->ch[0]!=nil)
link(x->ch[0],ll,mid,i);
if(x->ch[1]!=nil)
link(x->ch[1],mid+1,rr,i);
}
void read(){
root[0]=nil;
nil->ch[0]=nil->ch[1]=nil;
Read(n);
int i;
for(i=1;i<=n;i++){
Read(a[i]),Read(b[i]),Read(w[i]),Read(l[i]),Read(r[i]),Read(p[i]);
d[++m]=a[i],d[++m]=l[i],d[++m]=r[i];
ans+=w[i]+b[i];
}
sort(d+1,d+m+1);
m=unique(d+1,d+m+1)-d-1; //离散化一下,也可以不离散化
S=(n<<1)|1,T=S+1;
for(i=1;i<=n;i++){
a[i]=lower_bound(d+1,d+m+1,a[i])-d;
l[i]=lower_bound(d+1,d+m+1,l[i])-d;
r[i]=lower_bound(d+1,d+m+1,r[i])-d;
addedge(S,i,b[i]),addedge(i,T,w[i]),addedge(i,i+n,p[i]);
}
for(i=1;i<=n;i++){
if(root[i-1]!=nil)
link(root[i-1],1,m,i);
insert(root[i],root[i-1],1,m,i);
}
tot=T+tcnt-nil;
}
void bfs(){
q.push(T);
int u;
while(!q.empty()){
u=q.front();
q.pop();
for(node *p=adj[u];p;p=p->next){
if(!dist[p->v]){
dist[p->v]=dist[u]+1;
q.push(p->v);
}
}
}
dist[T]=0;
}
int dfs(int u,int augu){
if(u==T)
return augu;
int augv=0,v,mind=tot-1,delta;
for(node *p=adj[u];p;p=p->next){
v=p->v;
if(p->cap){
if(dist[u]==dist[v]+1){
delta=min(augu-augv,p->cap);
delta=dfs(v,delta);
augv+=delta;
p->cap-=delta;
p->back->cap+=delta;
if(augv==augu||dist[S]>=tot)
return augv;
}
mind=min(mind,dist[v]);
}
}
if(!augv){
if(!--vd[dist[u]])
dist[S]=tot;
dist[u]=mind+1;
vd[dist[u]]++;
}
return augv;
}
void sap(){
bfs(); //预处理dist数组,常数优化
for(int i=1;i<=tot;i++){
if(!dist[i]){
dist[i]=tot;
continue;
}
vd[dist[i]]++;
}
vd[dist[T]=0]++;
while(dist[S]int main()
{
read();
sap();
printf("%d\n",ans-flow);
}
现在,你已经掌握了A+B Problem这套理论