[ONTAK2015]Bajtman i Okrągły Robin
bzoj 4276: [ONTAK2015]Bajtman i Okrągły Robin
Time Limit: 40 Sec Memory Limit: 256 MBDescription
有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢走c[i]元。作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢?
Input
第一行包含一个正整数n(1<=n<=5000),表示强盗的个数。
接下来n行,每行包含三个正整数a[i],b[i],c[i](1<=a[i]<b[i]<=5000,1<=c[i]<=10000),依次描述每一个强盗。
Output
输出一个整数,即可以挽回的损失的最大值。
Sample Input
4
1 4 40
2 4 10
2 3 30
1 3 20
1 4 40
2 4 10
2 3 30
1 3 20
Sample Output
90
费用流
最容易想到的建图方法:
TLE
主要是因为一个强盗向一个区间内的所有点连边,太多了
所以我们考虑优化这些连边
线段树
建图方法:
然后跑最大费用即可
#include<cstdio> #include<queue> #include<algorithm> #include<cstring> #define N 50001 #define M N*11 #define inf 1e9 using namespace std; int n,a[N],b[N],c[N],maxn; int id[N*4],num,tot=1; int src,dec,ans; int front[N*6],from[M],next[M],to[M],cap[M],cost[M],pre[N*6]; int dis[N*6]; bool v[N*6]; int lc[N*6],rc[N*6]; queue<int>q; struct node { int l,r; }tr[N*4]; void add(int u,int v,int w,int val) { to[++tot]=v;next[tot]=front[u];front[u]=tot;from[tot]=u;cap[tot]=w;cost[tot]=val; to[++tot]=u;next[tot]=front[v];front[v]=tot;from[tot]=v;cap[tot]=0;cost[tot]=-val; } void build(int k,int l,int r) { id[k]=++num; tr[k].l=l;tr[k].r=r; if(l==r) return; int mid=l+r>>1; build(k<<1,l,mid); build((k<<1)+1,mid+1,r); add(id[k<<1],id[k],mid-l+1,0); add(id[(k<<1)+1],id[k],r-mid,0); } void insert(int k,int l,int r,int wh) { if(tr[k].l==l&&tr[k].r==r) { add(wh,id[k],1,c[wh-num]); return; } int mid=tr[k].l+tr[k].r>>1; if(r<=mid) insert(k<<1,l,r,wh); else if(l>mid) insert((k<<1)+1,l,r,wh); else {insert(k<<1,l,mid,wh);insert((k<<1)+1,mid+1,r,wh);} } bool spfa() { memset(dis,-127,sizeof(dis)); q.push(src);v[src]=true;dis[src]=0; while(!q.empty()) { int now=q.front();q.pop();v[now]=false; for(int i=front[now];i;i=next[i]) { int t=to[i]; if(dis[t]<dis[now]+cost[i]&&cap[i]>0) { dis[t]=dis[now]+cost[i]; pre[t]=i; if(!v[t]) { q.push(t); v[t]=true; } } } } return dis[dec]>0; } void mcmf() { while(spfa()) { int tmp=inf; for(int i=pre[dec];i;i=pre[from[i]]) tmp=min(tmp,cap[i]); for(int i=pre[dec];i;i=pre[from[i]]) { cap[i]-=tmp;cap[i^1]+=tmp; } ans+=tmp*dis[dec]; } printf("%d",ans); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d%d",&a[i],&b[i],&c[i]); b[i]--; maxn=max(maxn,b[i]); } dec=n*6-1; build(1,1,maxn); for(int i=1;i<=n;i++) add(src,num+i,1,0); add(1,dec,maxn,0); for(int i=1;i<=n;i++) insert(1,a[i],b[i],i+num); mcmf(); }
dis数组开始初始化为-1,应该是-inf,因为反向弧的费用为负值,未访问过的点要足够小才能完成回流