bzoj2257[POI2011]Programming Contest

首先可以费用流建图,左边一堆点表示人,右边一堆点表示题,源点向每个人连floor(t/r)条边,费用依次为r,2r,3r….然后写了一个卡不过去,动态加边也卡不过去,然后我想:这里一定有一些不为人知的卡常黑科技!然后去查题解发现不是费用流…因为只有源点向人的连边有费用,那么费用流的过程其实是:考虑让尽量多的人做费用为r的第一道题,然后让尽量多的人做费用为2r的第二道题…然后我们发现,写一个动态加边的dinic就可以了…我这里没有加边,直接把源点向人连边的边权重置,效果是一样的.

边权重置之后就不能沿着反向边反悔了,但这道题的性质使得不考虑反悔的情况也是对的.如果考虑费用为r的时候让1号人做了1号题,然后考虑费用为2r的时候发现可以让2号人做1号题,1号人做2号题,这种情况是不会出现的,因为在考虑费用为r的时候就可以让2号人做1号题,1号人做2号题.

一开始跑费用流T了好几发,然后dinic当前弧优化写残了又T了好几发...

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1005,maxm=1000005;
struct edge{
  int to,next,w;
}lst[maxm];int len=0,first[maxn],_first[maxn];
void addedge(int a,int b,int w){
  lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;
  lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
int q[maxn],dis[maxn],vis[maxn],s,t,head,tail,T;
bool bfs(){
  head=tail=0;vis[s]=++T;dis[s]=0;q[tail++]=s;
  while(head!=tail){
    int x=q[head++];
    for(int pt=first[x];pt!=-1;pt=lst[pt].next){
      if(lst[pt].w&&vis[lst[pt].to]!=T){
    dis[lst[pt].to]=dis[x]+1;vis[lst[pt].to]=T;q[tail++]=lst[pt].to;
      }
    }
  }
  if(vis[t]==T)memcpy(_first,first,sizeof(first));
  return vis[t]==T;
}
int dfs(int x,int lim){
  if(x==t)return lim;
  int flow=0,a;
  for(int pt=_first[x];pt!=-1;pt=lst[pt].next){
    _first[x]=pt;
    if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){
      lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;
      if(lim==flow)return lim;
    }
  }
  return flow;
}
int dinic(){
  int ans=0,x;
  while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;
  return ans;
}
int n,m,r,lim,k;
int main(){
  memset(first,-1,sizeof(first));
  scanf("%d%d%d%d%d",&n,&m,&r,&lim,&k);
  s=0;t=n+m+1;
  for(int i=1;i<=m;++i)addedge(n+i,t,1);
  int a,b;
  for(int i=1;i<=k;++i){
    scanf("%d%d",&a,&b);
    addedge(a,n+b,1);
  }
  int ans1=0,ans2=0;
  for(int i=1;i<=n;++i)addedge(s,i,1);
  for(int j=1;j*r<=lim;++j){
    int tmp=dinic();ans1+=tmp;ans2+=tmp*j*r;
    if(tmp==0)break;
    for(int i=1;i<=n;++i){
      lst[len-i*2].w=1;lst[len-i*2+1].w=0; 
    }
  }
  printf("%d %d\n",ans1,ans2);
  return 0;
}

 

posted @ 2017-02-14 11:27  liu_runda  阅读(368)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难