bzoj2040 [2009国家集训队]拯救Protoss的故乡
题解:
大意是$0$为源,所有叶子节点为汇,求费用在$m$以内的最大流。
直接跑网络流会$T$。
考虑树形$dp$,发现自己不会。
考虑贪心,每次找一个路径总费用最小的叶子结点(每条边费用为$0$或$1$或$inf$),然后求出路径最大容量,
整条树链都减掉最大容量。
输入时每条边有$(a,b)$,代表三种状态:
费用为$0$,最大容量为$a$;
费用为$1$,最大容量为$b$;
费用为$inf$,最大容量$inf$。
操作涉及到区间求最小值,区间减法,用树剖+线段树维护即可。
貌似是$WC2019$讲的模拟费用流。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 10050; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } ll m; int n,hed[N],cnt; struct EG { int to,nxt,a,b; }e[N]; void ae(int f,int t,int a,int b) { e[++cnt].to = t; e[cnt].nxt = hed[f]; e[cnt].a = a; e[cnt].b = b; hed[f] = cnt; } int dep[N],siz[N],fa[N],son[N],top[N],tin[N],tout[N],pla[N],tim,otd[N],A[N],B[N]; struct Pair { ll x,y; Pair(){x=0x3f3f3f3f;} Pair(int x,int y):x(x),y(y){} void operator += (const int&a){x+=a;} bool operator < (const Pair&a){return x<a.x;} }; struct segtree { ll tag[N<<2]; Pair v[N<<2]; void update(int u) { v[u] = v[u<<1]<v[u<<1|1]?v[u<<1]:v[u<<1|1]; } void add(int u,int d) { v[u]+=d; tag[u]+=d; } void pushdown(int u) { if(tag[u]) { add(u<<1,tag[u]); add(u<<1|1,tag[u]); tag[u] = 0; } } void insert(int l,int r,int u,int ql,int qr,ll d) { if(l==ql&&r==qr) { add(u,d); return ; } pushdown(u); int mid = (l+r)>>1; if(qr<=mid)insert(l,mid,u<<1,ql,qr,d); else if(ql>mid)insert(mid+1,r,u<<1|1,ql,qr,d); else insert(l,mid,u<<1,ql,mid,d),insert(mid+1,r,u<<1|1,mid+1,qr,d); update(u); } Pair query(int l,int r,int u,int ql,int qr) { if(l==ql&&r==qr)return v[u]; pushdown(u); int mid = (l+r)>>1; if(qr<=mid)return query(l,mid,u<<1,ql,qr); else if(ql>mid)return query(mid+1,r,u<<1|1,ql,qr); else { Pair p1 = query(l,mid,u<<1,ql,mid); Pair p2 = query(mid+1,r,u<<1|1,mid+1,qr); return p1<p2?p1:p2; } } void build(int l,int r,int u,bool typ) { if(l==r) { if(!typ)//edge { int x = pla[l]; if(A[x])v[u]=Pair(A[x],x); else if(B[x]-A[x])v[u]=Pair(B[x]-A[x],x); else v[u]=Pair(0x3f3f3f3f,x); }else//point { int x = pla[l]; if(!otd[x])v[u] = Pair(0,x); else v[u] = Pair(0x3f3f3f3f,x); } return ; } int mid = (l+r)>>1; build(l,mid,u<<1,typ); build(mid+1,r,u<<1|1,typ); update(u); } }tr0,tr1; void dfs1(int u,int f) { fa[u] = f; siz[u] = 1; dep[u] = dep[f]+1; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; dfs1(to,u); siz[u]+=siz[to]; if(siz[to]>siz[son[u]])son[u]=to; } } void dfs2(int u,int Top) { top[u] = Top;tin[u]=++tim;pla[tim]=u; if(son[u])dfs2(son[u],Top); for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(to!=son[u]) dfs2(to,to); } tout[u]=tim; } Pair fd(int u) { Pair Min_Edge = tr0.query(1,n+1,1,tin[top[u]],tin[u]); u = fa[top[u]]; while(u) { Pair tmp = tr0.query(1,n+1,1,tin[top[u]],tin[u]); Min_Edge = Min_Edge<tmp?Min_Edge:tmp; u = fa[top[u]]; } return Min_Edge; } int zt[N]; int main() { read(n),read(m); for(int f,t,a,b,i=1;i<=n;i++) { read(f),read(t),read(a),read(b); f++,t++; ae(f,t,a,b);otd[f]++; A[t]=a,B[t]=b; } dfs1(1,0),dfs2(1,1); tr0.build(1,n+1,1,0); tr1.build(1,n+1,1,1); for(int i=1;i<=cnt;i++) { int v = e[i].to; if(!e[i].a)tr1.insert(1,n+1,1,tin[v],tout[v],1),zt[v]=1; if(!e[i].b)tr1.insert(1,n+1,1,tin[v],tout[v],0x3f3f3f3f),zt[v]=2; } int ans = 0; while(1) { Pair now = tr1.query(1,n+1,1,1,n+1); int u = now.y; Pair Min_Edge = fd(u); ll d = Min_Edge.x; if(now.x)d=min(d,m/now.x); if(!d)break; m-=now.x*d; ans+=d; while(u) { tr0.insert(1,n+1,1,tin[top[u]],tin[u],-d); Pair tmp = tr0.query(1,n+1,1,tin[top[u]],tin[u]); while(!tmp.x) { int v = tmp.y; if(!zt[v]) { tr0.insert(1,n+1,1,tin[v],tin[v],B[v]-A[v]); tr1.insert(1,n+1,1,tin[v],tout[v],1); }else if(zt[v]==1) { tr0.insert(1,n+1,1,tin[v],tout[v],0x3f3f3f3f);// tr1.insert(1,n+1,1,tin[v],tout[v],0x3f3f3f3f); } zt[v]++; tmp = tr0.query(1,n+1,1,tin[top[u]],tin[u]); } u = fa[top[u]]; } } printf("%d\n",ans); return 0; }