[无源汇最大费用可行流 差分费用流] Codeforces 717G Bubble Cup 9 - Finals G. Underfail

类似志愿者招募的建图方法

用一个点表示差分 然后用流量表示出差分的和

然后在l,r+1之间连边


这是个无源汇最大费用可行流

我是这么做的

建个超级源汇

让每条正权的边流满

这些流通过超级源汇流

那么流量不平衡

所以再跑遍T-S的最小费用最大流

因为是最大流,所以源汇的流量都为零 最小费用,也就是反悔最少,费用最大


#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s){
  int len=0; char c=nc();
  for (;!(c>='a' && c<='z');c=nc());
  for (;c>='a' && c<='z';s[++len]=c,c=nc()); s[++len]=0; return len-1;
}

const int N=505;

struct edge{
  int u,v,w,f,next;
}G[500005];
int head[N],inum=1;

inline void add(int u,int v,int w,int f,int p){
  G[p].u=u; G[p].v=v; G[p].w=w; G[p].f=f; G[p].next=head[u]; head[u]=p;
}

inline void link(int u,int v,int w,int f){
  add(u,v,w,f,++inum); add(v,u,-w,0,++inum);
}

int S,T;
int dis[N],ins[N],pre[N];
const int NQ=1000005;
#define ad(x) ((x)+1==NQ?(x)=0:++(x))
int Q[NQ],l,r;
int Maxcost;
#define V G[p].v
inline bool SPFA(){
  for (int i=1;i<=T;i++) dis[i]=1<<30,ins[i]=0,pre[i]=0;
  l=-1,r=-1;
  Q[ad(r)]=S; dis[S]=0; ins[S]=1;
  while (l!=r){
    int u=Q[ad(l)]; ins[u]=0;
    for (int p=head[u];p;p=G[p].next)
      if (G[p].f && dis[V]>dis[u]+G[p].w){
	dis[V]=dis[u]+G[p].w; pre[V]=p;
	if (!ins[V]) Q[ad(r)]=V,ins[V]=1;
      }
  }
  if (dis[T]==1<<30) return false;
  int minimum=1<<30;
  for (int p=pre[T];p;p=pre[G[p].u])
    minimum=min(minimum,G[p].f);
  for (int p=pre[T];p;p=pre[G[p].u]){
    G[p].f-=minimum; G[p^1].f+=minimum;
    Maxcost-=minimum*G[p].w;
  }
  return true;
}

int n,m;
char str[N]; int nxt[N];
int len[N],cost[N]; char s[N][N];

inline void addedge(int l,int r,int cost){
  r++;
  link(l,r,cost,1);
  link(r,S,0,1);
  link(T,l,0,1);
  Maxcost+=cost;
}

inline void KMP(int len,char *s,int cost){
  nxt[1]=0; int k=0;
  for (int i=2;i<=len;i++){
    while (k && s[k+1]!=s[i]) k=nxt[k];
    if (s[k+1]==s[i]) k++;
    nxt[i]=k;
  }
  k=0;
  for (int i=1;i<=n;i++){
    while (k && s[k+1]!=str[i]) k=nxt[k];
    if (s[k+1]==str[i]) k++;
    if (k==len){
      addedge(i-len+1,i,cost);
      k=nxt[k];
    }
  }
}

int main(){
  int tem;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(str);
  read(m);
  for (int i=1;i<=m;i++)
    len[i]=read(s[i]),read(cost[i]);
  read(tem);
  for (int i=1;i<=n;i++)
    link(i,i+1,0,tem);
  S=n+1+1; T=n+1+2;
  for (int i=1;i<=m;i++)
    KMP(len[i],s[i],cost[i]);
  swap(S,T);
  while (SPFA());
  printf("%d\n",Maxcost);
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值