LOADING

加载过慢请开启缓存,浏览器默认开启

daily 3

2023/5/20 daily

两道网络流,一道dp

CF 1423B

比较裸的网络流,我们二分这个最短工期,每次用最大流跑一次二分图匹配就好了

struct Dinic
{
  struct Edge
  {
    int from,to,cap,flow;
    Edge(int f,int t,int c,int w):from(f),to(t),cap(c),flow(w){}
  };
  int n,m,s,t;//size of point and line and the start point and end point
  vector<int>d,cur;
  vector<bool>vis;
  vector<Edge>E;
  vector<vector<int>>G;
  const int INF=0x3f3f3f3f;
  Dinic(int n,int m):n(n),m(m)
  {
    d.resize(n+1);
    vis.resize(n+1,false);
    G.resize(n+1);
    cur.resize(n+1);
  }
  void add_edge(int from,int to,int cap)
  {
    E.push_back({from,to,cap,0});
    G[from].push_back(E.size()-1);
    E.push_back({to,from,0,0});
    G[to].push_back(E.size()-1); 
  }
  bool bfs()//get the depth of every point
  {
    fill(vis.begin(),vis.end(),false);
    queue<int>q;
    q.push(s);
    d[s]=0;
    vis[s]=1;
    while(!q.empty())
    {
      int x=q.front();
      q.pop();
      for(int i=0;i<G[x].size();++i)
      {
        auto& e=E[G[x][i]];
        if(!vis[e.to]&&e.cap>e.flow)
        {
          vis[e.to]=1;
          d[e.to]=d[x]+1;
          q.push(e.to);
        }
      }
    }
    return vis[t];
  }
  ll dfs(int x,int a)
  {
    if(x==t||a==0)return a;
    ll res=0,f;
    for(int& i=cur[x];i<G[x].size();++i)
    {
      auto& e=E[G[x][i]];
      if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
      {
        e.flow+=f;
        E[G[x][i]^1].flow-=f;
        res+=f;
        a-=f;
        if(a<=0)break;
      }
    }
    return res;
  }
  ll maxflow(int s,int t)
  {
    this->s=s;
    this->t=t;
    ll ans=0;
    while(bfs())
    {
      fill(cur.begin(),cur.end(),0);
      ans+=dfs(s,INF);//INF is the now flow
    }
    return ans;
  }
};
struct Road{
    int u,v,d;
};
void solve(){
  int n,m;
  cin>>n>>m;//total 2*n+2;
  vector<Road>E(m+1);
  int l=INT_MAX,r=INT_MIN;
  for(int i=1;i<=m;++i){
    cin>>E[i].u>>E[i].v>>E[i].d;
    l=min(l,E[i].d);
    r=max(r,E[i].d);
  }
  int source=2*n+1,sink=2*n+2;
  function<bool(int)>check=[&](int cur_time){
    Dinic dinic(2*n+3,0);
    for(int i=1;i<=n;++i){
        dinic.add_edge(source,i,1);
        dinic.add_edge(n+i,sink,1);
    }
    for(int i=1;i<=m;++i){
        if(E[i].d<=cur_time){
            dinic.add_edge(E[i].u,E[i].v+n,1);
        }
    }
    ll maxflow=dinic.maxflow(source,sink);
    return maxflow==n;
  };
  ll ans=-1;
  while(l<=r){
    int m=(l+r)>>1;
    if(check(m)){
        ans=m;
        r=m-1;
    }else{
        l=m+1;
    }
  }
  cout<<ans<<'\n';
  return;
}

CF 884F

最小费用流裸题,我们对每个S,向cnt[i]连一条容量为cnt[i]的边,然后因为是对称的位置,所以另起n/2个点,相互连线,最后n/2个点向汇点连线,之后跑最小费用流就好了

struct MCMF{
  struct edge{
    int u,v;
    ll cap,cost;
  };
  const ll INF=1e18;
  int s,t;//source and des
  ll mincostflow;//the MCMF answer
  ll maxflow;//the maxflow
  vector<edge>Edge;//all edges
  vector<vector<int>>E;//the graph
  vector<ll>dis;//distance between source and i
  vector<int>vis;//whether this point in the loose queue or not
  MCMF(int n,int source,int sink){
    E.resize(n+1);
    dis.resize(n+1);
    vis.resize(n+1);
    s=source;
    t=sink;
    mincostflow=0;
    maxflow=0;
  }
  void add_edge(int u,int v,int cap,int cost){
    E[u].push_back(Edge.size());
    Edge.push_back({u,v,cap,cost});
    E[v].push_back(Edge.size());
    Edge.push_back({v,u,0,-cost});
  }
  bool spfa(){
    fill(dis.begin(),dis.end(),INF);
    queue<int>q;
    q.push(s);
    vis[s]=1;
    dis[s]=0;
    while(!q.empty()){
      auto tmp=q.front();
      q.pop();
      vis[tmp]=0;
      for(int id:E[tmp]){
        auto e=Edge[id];
        int to=e.v;
        if(e.cap>0&&dis[to]>dis[tmp]+e.cost){
          dis[to]=dis[tmp]+e.cost;
          if(!vis[to]){
            q.push(to);
            vis[to]=1;
          }
        }
      }
    }
    return dis[t]!=INF;
  } 
  ll dfs(int u,ll flow){
    if(u==t)
      return flow;
    vis[u]=1;
    ll ans=0;
    for(int i=E[u].size()-1;i>=0;--i){
      int id=E[u][i];
      auto e=Edge[id];
      int v=e.v;
      if(e.cap&&!vis[v]&&dis[v]==dis[u]+e.cost){
        ll cur_flow=dfs(v,min(flow-ans,e.cap));//the flow of the remaining graph
        if(cur_flow){
          if(ans+cur_flow>flow)
            break;
          mincostflow+=cur_flow*e.cost;
          ans+=cur_flow;
          Edge[id^1].cap+=cur_flow;
          Edge[id].cap-=cur_flow;
        }
      }
    }
    vis[u]=0;
    return ans;
  }
  ll get_mcmf(){
    maxflow=0;
    while(spfa()){
      int cur_flow;
      while((cur_flow=dfs(s,INF)))maxflow+=cur_flow;
    }
    return mincostflow;
  }
};
void solve(){
  int n;
  cin>>n;
  string s;
  cin>>s;
  s=')'+s;
  vector<int>b(n+1);
  vector<int>cnt(26);
  ll sum=0;
  for(int i=1;i<=n;++i){
    cin>>b[i];
    sum+=b[i];
    cnt[s[i]-'a']++;
  }
  int source=n/2+26+1,sink=source+1;
  MCMF mf(n/2+26+3,source,sink);
  for(int i=0;i<26;++i){
    mf.add_edge(source,i+1,cnt[i],0);
    for(int j=1;j<=n/2;++j){
       int val=0;
       if(s[j]-'a'==i){
        val=max(val,b[j]);
       }
       if(s[n-j+1]-'a'==i){
        val=max(val,b[n-j+1]);
       }
       mf.add_edge(i+1,26+j,1,-val);
    }
  }
  for(int i=1;i<=n/2;++i){
    mf.add_edge(26+i,sink,2,0);
  }
  mf.get_mcmf();
  cout<<-mf.mincostflow<<'\n';
  return;
}

CF 497E
${dp[i][j]}$表示当前在i,用了j步,用前缀和和滚动数组优化一下就可以了

void solve(){
  int n,a,b,k;
  cin>>n>>a>>b>>k;
  vector<int>dp(n+1),sum(n+1);
  if(a>b){// you could only move in one side of b(up or down)
    n-=b;
    a-=b;
  }else{
    n=b-1;
    a=b-a;
  }
  dp[a]=1;
  for(int i=a;i<=n;++i)sum[i]=1;
  for(int i=1;i<=k;++i){
    for(int j=1;j<=n;++j){
        dp[j]=((sum[n]-sum[j>>1])-dp[j]+2*MOD)%MOD;
    }
    for(int j=1;j<=n;++j){
        sum[j]=(sum[j-1]+dp[j])%MOD;
    }
  }
  cout<<sum[n]<<'\n';
  return;
}