bzoj1577 [Usaco2009 Feb]庙会捷运Fair Shuttle
Description
公交车一共经过N(1<=N<=20000)个站点,从站点1一直驶到站点N。K(1<=K<=50000)群奶牛希望搭乘这辆公交车。第i群牛一共有Mi(1<=Mi<=N)只.
他们希望从Si到Ei去。
公交车只能座C(1<=C<=100)只奶牛。而且不走重复路线,请计算这辆车最多能满足多少奶牛听要求。
注意:对于每一群奶牛,可以部分满足,也可以全部满足,也可以全部不满足。
Input
第1行: 三个整数: K,N,C。 由空格隔开。
第2..K+1行:第i+1行,告诉你第i组奶牛的信息: S_i, E_i and M_i。由空格隔开。
Output
一行:可以在庙会乘坐捷运的牛的最大头数
Sample Input
8 15 3
1 5 2
13 14 1
5 8 3
8 14 2
14 15 1
9 12 1
12 15 2
4 6 1
1 5 2
13 14 1
5 8 3
8 14 2
14 15 1
9 12 1
12 15 2
4 6 1
Sample Output
10
HINT
捷运可以把2头奶牛从展台1送到展台5,3头奶牛从展台5到展台8, 2头奶牛从展台8 到展台14,1头奶牛从展台9送到展台12,一头奶牛从展台13送到展台14, 一头奶牛从 14送到15。
思路:这题可以费用流构图,是一个经典的费用流。不过超时了,惨惨。
建立源、汇点和另外的n个点。连结点i->点(i+1),容量为INF,费用为零。(1<=i<n)
对于每个区间i,连结点Si->点Ei,容量为Mi,费用为1.
连接源点S->点1,容量为C,费用为零。
连接点n->汇点T,容量为C,费用为零。
求S到T的最大费用最大流,输出费用即可。
下面是我的费用流代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define rep(i,a,b) for(int i=a;i<=b;i++) 4 #define Rep(i,a,b) for(int i=a;i>=b;i--) 5 #define gc() getchar() 6 #define ms(i,a) memset(a,i,sizeof(a)) 7 template<class T>void read(T &x){ 8 x=0; char c=0; 9 while (!isdigit(c)) c=gc(); 10 while (isdigit(c)) x=x*10+(c^48),c=gc(); 11 } 12 int const N=20000+3; 13 int const K=50000+3; 14 int const inf=1e8; 15 int S,T,n,k,c,cnt,h[N],p[N],vis[N],dist[N],f[N],q[N]; 16 struct edge{ 17 int to,nt,fl,ct,fr; 18 }e[K*5]; 19 void add(int a,int b,int fl,int ct){ 20 e[cnt]=(edge){b,h[a],fl,ct,a};h[a]=cnt++; 21 } 22 inline inc(int &x){if(x+1>=N) x=0;else x++;} 23 int spfa(){ 24 ms(-1,dist); 25 ms(0,vis); 26 ms(0,f); 27 f[S]=inf; 28 vis[S]=1; 29 q[0]=S; 30 dist[S]=0; 31 int l=0,r=1; 32 while (l^r){ 33 int x=q[l]; 34 vis[x]=0; 35 inc(l); 36 for(int i=h[x];i!=-1;i=e[i].nt){ 37 int v=e[i].to; 38 if(e[i].fl==0) continue; 39 if (dist[v]<dist[x]+e[i].ct){ 40 dist[v]=dist[x]+e[i].ct; 41 p[v]=i; f[v]=min(f[x],e[i].fl); 42 if(!vis[v]){ 43 vis[v]=1; 44 q[r]=v; 45 inc(r); 46 } 47 } 48 } 49 } 50 return f[T]; 51 } 52 int main(){ 53 read(k); 54 read(n); 55 read(c); 56 S=0,T=n+1; 57 ms(-1,h); 58 while (k--){ 59 int a,b,c; 60 read(a); read(b);read(c); 61 add(a,b,c,1); 62 add(b,a,0,-1); 63 } 64 add(S,1,c,0); 65 add(1,S,0,0); 66 add(n,T,c,0); 67 add(T,n,0,0); 68 rep(i,1,n-1) add(i,i+1,c,0),add(i+1,i,0,0); 69 int t,ans=0; 70 while ((t=spfa())){ 71 for(int i=T;i!=S;i=e[p[i]].fr){ 72 e[p[i]].fl-=t; 73 e[p[i]^1].fl+=t; 74 } 75 ans+=t*dist[T]; 76 } 77 printf("%d\n",ans); 78 return 0; 79 }
接下来我们考虑贪心的解法,我们发现结束时间早的尽量先安排,那么这题就是最优的。于是我们可以按照结束时间排序一下。 然后查找当前区间的最大值,可以安排的位置数量就是c减去这个最大值。 用了线段树查找区间的最小值。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define rep(i,a,b) for(int i=a;i<=b;i++) 4 #define Rep(i,a,b) for(int i=a;i>=b;i--) 5 #define gc() getchar() 6 #define ms(i,a) memset(a,i,sizeof(a)) 7 #define mid (l+r>>1) 8 #define lc (x<<1) 9 #define rc (x<<1|1) 10 template<class T>void read(T &x){ 11 x=0; char c=0; 12 while (!isdigit(c)) c=gc(); 13 while (isdigit(c)) x=x*10+(c^48),c=gc(); 14 } 15 int const N=20000+3; 16 int const K=50000+3; 17 struct Q{ 18 int x,y,z; 19 bool operator <(const Q &rhs) const { return y<rhs.y; } 20 }a[K]; 21 int n,k,c,tg[N<<2],s[N<<2]; 22 void pushdown(int x){ 23 s[lc]+=tg[x]; 24 s[rc]+=tg[x]; 25 tg[lc]+=tg[x]; 26 tg[rc]+=tg[x]; 27 tg[x]=0; 28 } 29 int query(int x,int l,int r,int ll,int rr){ 30 if(ll<=l && r<=rr) return s[x]; 31 pushdown(x); 32 if(rr<=mid) return query(lc,l,mid,ll,rr); 33 else if(ll>mid) return query(rc,mid+1,r,ll,rr); 34 else return max(query(lc,l,mid,ll,rr),query(rc,mid+1,r,ll,rr)); 35 } 36 void update(int x,int l,int r,int ll,int rr,int v){ 37 if(ll<=l && r<=rr){ 38 s[x]+=v; 39 tg[x]+=v; 40 return ; 41 } 42 pushdown(x); 43 if(ll<=mid) update(lc,l,mid,ll,rr,v); 44 if(rr>mid) update(rc,mid+1,r,ll,rr,v); 45 s[x]=max(s[lc],s[rc]); 46 } 47 int main(){ 48 read(k); 49 read(n); 50 read(c); 51 rep(i,1,k){ 52 read(a[i].x); read(a[i].y); read(a[i].z); 53 a[i].y--; 54 } 55 sort(a+1,a+k+1); 56 int ans=0; 57 rep(i,1,k){ 58 int t=min(a[i].z,c-query(1,1,n,a[i].x,a[i].y)); 59 ans+=t; 60 update(1,1,n,a[i].x,a[i].y,t); 61 } 62 printf("%d\n",ans); 63 return 0; 64 }