タイトル
効果の対象に
あなたのツリーネットワークを与え、各エッジは父親から息子を流します。ルート起源、シンク容量にリーフノードの流れは無限大です。
私たちは、拡大の合計まで、容量を拡張するために、いくつかのエッジを与えることができます\(m個\)の容量を。各側は容量制限があります。
最大のストリームの最大容量を拡張するために探しています。
歴史上のものがたり
漠然と推測正のソリューションは、チェーンツリー分割としなければならないが、再生できませんでしたが、プレイする時間がありません。
DPの暴力だけで湿気することができます。
セット\(H_ {I、J} \) の\は、(私は\)父親である\(Iは\)最大流量の、拡張\(J \)倍の容量。\(G_ {I、J} \) の\(Iは\)拡大する最大流量サブツリー\(J \)倍の容量。前者は後者の容量とエッジによって得られた最小値をとります。
伝達方程式は明らかです。
このように、水(\ 70 \)点、予想以上\(20 \)ポイント。
正解
費用流アプローチがあります:息子にそれぞれ異なる父のためにも、二つの側面を要しました。もう少しコストの同等いったん展開します。
最小コストの最大値、増補選挙費用の最小それぞれの道を実行して、それが以下で費用はかかりません\(m個\)最大流量が答えたときです。
このアプローチはある\(O(N ^ 2) \) 。各側が拡張した後、少なくとも完全な流れが存在することになるので、同等のこのエッジを削除しました。
解説所望のタイトルのスコアは\(\ 100) ...... ......しかし、私は本当に......以上に本当に、このような戦いを誰に仕え
正解は......費用流問題を最適化するためのプロパティツリーの使用による解説です\(LCT \)を、私は人々が、物流の問題に対する解決策をプレイ見すぎです。これはまた遊んで、明らかに、このような長いタイトルのためのコードの量です\(LCTを\) ?
次は費用流プロセスをシミュレートします:
- パスの葉ノードへのルートノードから最小限のコストを検索します。
- これは、最大流路取得\(F \) (パスの最小側、すなわち容量)。
- 答えに統計。
- パスのすべての側面で容量が減算される(\のF \) 。
- 修飾された経路上の全流量(即ち、容量外を見ながら\(0 \)側)。カテゴリー話は:コストがある場合は、\(0 \) 、それはコスト変更\(1 \)を、そしてその容量を変更し、コストがされている場合(1 \)\(つまり、通過することができない、このエッジをマークし、すべての子孫)が到達することはできません。
もちろん、特別審査員を見て覚えていることよりも大きくしようとしている\(m個\)状況。
ツリーの分割及び鎖セグメントツリーを持つこの事は維持します。ツリーラインのメンテナンス情報:最小容量\(のF \)と彼の息子の数の\(NUMFの\) (上から最小容量の下端までのストリップのパス)だけでなく、最小コストの\(\ワット)とリーフノード番号\(numwの\) (リーフ・ノード・パスと報酬へのルート・ノードのための料金)。
操作\(1 \)最小依頼することです\(W \) 。
操作\(2 \)現在のノードが最小のルートパスであり、\(F \) 。
操作\(4 \)現在のノードが保存するルート・パスである全てのエッジ間隔\(F \) 。
操作\(5 \)コストは、現在のノードのすべてのリーフノード間隔に追加することができている\(1 \)または無限。
これらの操作は、ある......
コード
ロング......幸い、木の分割と鎖セグメントツリー良い戦い......
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cassert>
#define N 10010
#define INF 1000000000
int n,m;
struct EDGE{
int to;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
bool leaf[N];
int a[N],b[N];
int fa[N],siz[N],hs[N];
int top[N],dfn[N],nowdfn,to_num[N];
int noww[N];
void dfs1(int x){
siz[x]=1;
for (EDGE *ei=last[x];ei;ei=ei->las){
fa[ei->to]=x;
dfs1(ei->to);
siz[x]+=siz[ei->to];
if (siz[ei->to]>siz[hs[x]])
hs[x]=ei->to;
}
if (siz[x]==1)
leaf[x]=1;
}
void dfs2(int x,int t){
top[x]=t;
dfn[x]=++nowdfn;
to_num[nowdfn]=x;
if (hs[x])
dfs2(hs[x],t);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=hs[x])
dfs2(ei->to,ei->to);
}
struct Node{
int f,numf;
int w,numw;
int tagf,tagw;
} seg[N*4];
inline void updatef(int k){
if (seg[k<<1].f<=seg[k<<1|1].f)
seg[k].f=seg[k<<1].f,seg[k].numf=seg[k<<1].numf;
else
seg[k].f=seg[k<<1|1].f,seg[k].numf=seg[k<<1|1].numf;
}
inline void updatew(int k){
if (seg[k<<1].w<=seg[k<<1|1].w)
seg[k].w=seg[k<<1].w,seg[k].numw=seg[k<<1].numw;
else
seg[k].w=seg[k<<1|1].w,seg[k].numw=seg[k<<1|1].numw;
}
inline void pushdown(int k){
if (seg[k].tagf){
seg[k<<1].f+=seg[k].tagf;
seg[k<<1|1].f+=seg[k].tagf;
seg[k<<1].tagf+=seg[k].tagf;
seg[k<<1|1].tagf+=seg[k].tagf;
seg[k].tagf=0;
}
if (seg[k].tagw){
if (seg[k].tagw>=INF)
seg[k<<1].w=seg[k<<1|1].w=seg[k<<1].tagw=seg[k<<1|1].tagw=INF;
else{
seg[k<<1].w+=seg[k].tagw;
seg[k<<1|1].w+=seg[k].tagw;
seg[k<<1].tagw+=seg[k].tagw;
seg[k<<1|1].tagw+=seg[k].tagw;
seg[k].tagw=0;
}
}
}
void build(int k,int l,int r){
if (l==r){
if (leaf[to_num[l]])
seg[k]={a[to_num[l]],to_num[l],0,to_num[l],0,0};
else
seg[k]={a[to_num[l]],to_num[l],INF,0,0,0};
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
updatef(k),updatew(k);
}
void changef(int k,int l,int r,int st,int en,int c){
if (st<=l && r<=en){
seg[k].f+=c;
seg[k].tagf+=c;
return;
}
pushdown(k);
int mid=l+r>>1;
if (st<=mid)
changef(k<<1,l,mid,st,en,c);
if (mid<en)
changef(k<<1|1,mid+1,r,st,en,c);
updatef(k);
}
void changew(int k,int l,int r,int st,int en,int c){
if (st<=l && r<=en){
if (c>=INF)
seg[k].w=seg[k].tagw=INF;
else{
seg[k].w+=c;
seg[k].tagw+=c;
}
return;
}
pushdown(k);
int mid=l+r>>1;
if (st<=mid)
changew(k<<1,l,mid,st,en,c);
if (mid<en)
changew(k<<1|1,mid+1,r,st,en,c);
updatew(k);
}
pair<int,int> un(const pair<int,int> &a,const pair<int,int> &b){return a.first<=b.first?a:b;}
pair<int,int> queryf(int k,int l,int r,int st,int en){
if (st<=l && r<=en)
return {seg[k].f,seg[k].numf};
pushdown(k);
int mid=l+r>>1;
pair<int,int> res(INF,0);
if (st<=mid)
res=un(res,queryf(k<<1,l,mid,st,en));
if (mid<en)
res=un(res,queryf(k<<1|1,mid+1,r,st,en));
return res;
}
int queryw(int k,int l,int r,int x){
if (l==r)
return seg[k].tagw;
pushdown(k);
int mid=l+r>>1;
if (x<=mid)
return queryw(k<<1,l,mid,x);
return queryw(k<<1|1,mid+1,r,x);
}
inline int flow(int x){
int res=INT_MAX;
for (;x;x=fa[top[x]])
res=min(res,queryf(1,1,n,dfn[top[x]],dfn[x]).first);
return res;
}
inline void find(int x,int c){
for (int y=x;y;y=fa[top[y]])
changef(1,1,n,dfn[top[y]],dfn[y],-c);
for (;x;x=fa[top[x]]){
while (x){
pair<int,int> tmp=queryf(1,1,n,dfn[top[x]],dfn[x]);
if (tmp.first)
break;
if (noww[tmp.second]==0){
noww[tmp.second]=1;
changew(1,1,n,dfn[tmp.second],dfn[tmp.second]+siz[tmp.second]-1,1);
changef(1,1,n,dfn[tmp.second],dfn[tmp.second],b[tmp.second]);
}
else{
changew(1,1,n,dfn[tmp.second],dfn[tmp.second]+siz[tmp.second]-1,INF);
x=fa[tmp.second];
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
a[1]=INT_MAX;
for (int i=1;i<=n;++i){
int u,v;
scanf("%d%d",&u,&v);
u++,v++;
e[ne]={v,last[u]};
last[u]=e+ne++;
scanf("%d%d",&a[v],&b[v]);
b[v]=min(b[v]-a[v],m);
}
n++;
dfs1(1),dfs2(1,1);
build(1,1,n);
int ans=0;
while (1){
int x=seg[1].numw,cost=seg[1].w;
if (cost>=INF)
break;
int plus=flow(x);
if (m<cost*plus){
ans+=m/cost;
break;
}
m-=cost*plus;
ans+=plus;
find(x,plus);
}
printf("%d\n",ans);
return 0;
}
概要
時には、ネットワークフローデータ構造を維持することができる......