题目:
https://www.luogu.org/problemnew/show/P3749
分析:
显然选择了一个区间 [ l , r ] [l,r] [l,r],那么一定要选他的子区间。因为区间的价值有正负,所以考虑最大权闭合子图。
即 S S S向每个权值为正区间 [ l , r ] [l,r] [l,r]连边, [ l , r ] [l,r] [l,r]向负区间连边,每个区间 [ l , r ] [l,r] [l,r]向 [ l + 1 , r ] [l+1,r] [l+1,r]和 [ l , r − 1 ] [l,r-1] [l,r−1]连边。
我们发现断掉负权点的边相当于选择吃这一个区间,那么也会有费用。
每个区间向区间内的每一种食物连inf的边,每种食物向T连 x x x的边,其中 x x x为类型。每种食物向他的类型连inf的边,每种类型向T连 m ∗ x 2 m*x^2 m∗x2的边。
这样一来,我们需要同时断掉三个部分才能代表吃的部分。先要扣掉美味值,还要付类型费用,每一个食物还要付独立费用。
代码:
// luogu-judger-enable-o2
#include
#include
#include
#include
#define LL long long
const int maxn=107;
const int N=2e4+7;
const LL inf=1e13;
using namespace std;
int n,m,x,cnt,s,t,tot;
LL ans;
int id[maxn][maxn],w[maxn];
int vis[N],num[N],ls[N],dis[N];
struct edge{
int y,op,next;
LL w;
}g[N*50];
queue q;
void add(int x,int y,LL w)
{
g[++tot]=(edge){y,tot+1,ls[x],w};
ls[x]=tot;
g[++tot]=(edge){x,tot-1,ls[y],0};
ls[y]=tot;
}
bool bfs()
{
for (int i=s;i<=t;i++) dis[i]=0x3f3f3f3f;
while (!q.empty()) q.pop();
dis[s]=0;
q.push(s);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]>dis[x]+1))
{
dis[y]=dis[x]+1;
if (y==t) return true;
q.push(y);
}
}
}
return false;
}
LL dfs(int x,LL maxf)
{
if ((x==t) || (!maxf)) return maxf;
LL ret=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]==dis[x]+1))
{
LL f=dfs(y,min(maxf-ret,g[i].w));
if (!f) dis[y]=-1;
g[i].w-=f;
g[g[i].op].w+=f;
ret+=f;
if (ret==maxf) break;
}
}
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
for (int j=i;j<=n;j++)
{
id[i][j]=++cnt;
}
}
int now=cnt;
for (int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
if (!vis[w[i]])
{
vis[w[i]]=++cnt;
num[cnt-now]=w[i];
}
}
s=0,t=cnt+n+1;
for (int i=1;i<=n;i++)
{
for (int j=i;j<=n;j++)
{
scanf("%d",&x);
if (x>0) add(s,id[i][j],x),ans+=(LL)x;
else add(id[i][j],t,-x);
for (int k=i;k<=j;k++) add(id[i][j],cnt+k,inf);
if (i