【六省联考2017】寿司餐厅
题面
题解
有经验的选手可以根据权值不重复计算的特性看出这是一个最大权闭合子图问题;
我看了题解就知道这是一个最大权闭合子图问题。。。
建边:
-
对于所有的区间$[i,j]$的收益,将它们都看成点
如果权值为正,从源点连来,容量为权值
否则连向汇点,容量为权值的绝对值
-
将区间$[i,j]$,向区间内$i,j$连边,容量为$\infty$,表示选对应的寿司就选对应的区间
-
对于所有寿司类型$a[i]$,将它们向汇点连边,容量为$m\times a[i]^2$
-
对于每一个寿司,向它们所属类型连边,容量$\infty$,向汇点连边,容量为$a[i]$
-
对于所有的$[i,j]$区间,向$[i + 1,j]$和$[i, j - 1]$,容量为$\infty$,表示选了大区间肯定选其中的小区间
$ans$就是总收益减去最小割就可以了
代码
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define clear(x, y) memset(x, y, sizeof(x))
inline int read()
{
int data = 0, w = 1; char ch = getchar();
while(ch != '-' && (!isdigit(ch))) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
}
const int maxn(40010), maxm(110), INF(0x3f3f3f3f);
struct edge { int next, to, cap; } e[maxn << 6];
int head[maxn], cur[maxn], e_num = -1;
inline void add_edge(int from, int to, int cap)
{
e[++e_num] = (edge) {head[from], to, cap}; head[from] = e_num;
e[++e_num] = (edge) {head[to], from, 0 }; head[to] = e_num;
}
bool bfs();
int dfs(int x, int f);
inline int Dinic();
int n, m, a[maxm], g[maxm][maxm];
int id[maxm][maxm], ida[maxn], idp[maxm], S, T;
long long ans;
void init_id()
{
int idcnt = 0; S = ++idcnt;
for(RG int i = 1; i <= n; i++)
for(RG int j = i; j <= n; j++)
id[i][j] = ++idcnt;
for(RG int i = 1; i <= n; i++) if(!ida[a[i]]) ida[a[i]] = ++idcnt;
for(RG int i = 1; i <= n; i++) idp[i] = ++idcnt;
T = ++idcnt;
}
void build_edge()
{
static bool vis[maxn]; clear(vis, 0); init_id();
for(RG int i = 1; i <= n; i++)
if(!vis[a[i]])
{
vis[a[i]] = true;
add_edge(ida[a[i]], T, m * a[i] * a[i]);
}
for(RG int i = 1; i <= n; i++)
add_edge(idp[i], ida[a[i]], INF),
add_edge(idp[i], T, a[i]);
for(RG int i = 1; i <= n; i++)
for(RG int j = i; j <= n; j++)
{
if(g[i][j] > 0)
{
ans += g[i][j];
add_edge(S, id[i][j], g[i][j]);
}
else if(g[i][j] < 0) add_edge(id[i][j], T, -g[i][j]);
add_edge(id[i][j], idp[i], INF);
add_edge(id[i][j], idp[j], INF);
if(i != j)
add_edge(id[i][j], id[i + 1][j], INF),
add_edge(id[i][j], id[i][j - 1], INF);
}
}
int main()
{
clear(head, -1); n = read(), m = read();
for(RG int i = 1; i <= n; i++) a[i] = read();
for(RG int i = 1; i <= n; i++)
for(RG int j = i; j <= n; j++) g[i][j] = read();
build_edge();
printf("%lld\n", ans - Dinic());
return 0;
}
/**************** Dinic ******************/
inline int Dinic()
{
int res = 0;
while(bfs())
{
for(RG int i = S; i <= T; i++) cur[i] = head[i];
res += dfs(S, INF);
}
return res;
}
int lev[maxn], q[maxn], tail;
bool bfs()
{
clear(lev, 0);
q[tail = lev[S] = 1] = S;
for(RG int i = 1; i <= tail; i++)
{
int x = q[i];
for(RG int j = head[x]; ~j; j = e[j].next)
{
int to = e[j].to; if(lev[to] || (!e[j].cap)) continue;
q[++tail] = to, lev[to] = lev[x] + 1;
}
}
return lev[T];
}
int dfs(int x, int f)
{
if(x == T || (!f)) return f;
int ans = 0, cap;
for(RG int &i = cur[x]; ~i; i = e[i].next)
{
int to = e[i].to;
if(e[i].cap && lev[to] == lev[x] + 1)
{
cap = dfs(to, std::min(f - ans, e[i].cap));
e[i].cap -= cap, e[i ^ 1].cap += cap;
ans += cap; if(ans == f) break;
}
}
return ans;
}