CF2038H Galactic Council 题解
题目描述
有 \(n\) 个政党,初始每个政党的权力为 \(0\) ,并且没有统治党。
接下来 \(m\) 轮操作:
-
你可以选择一个政党,并给它的权力增加 \(1\) 。但不能选择统治党。
如果你在第 \(j\) 轮选择了第 \(i\) 个政党,则可以获得 \(a_{i,j}\) 的收益。
-
权力最大的政党会成为统治党,如果有多个政党符合要求,则取编号最小的一个。
-
你需要保证第 \(j\) 轮操作结束的统治党为 \(p_j\) 。
求你能获得的最大收益,无解输出 -1 ,否则输出每天选择的政党。
数据范围
- \(2\le n,m\le 50\) 。
- \(1\le p_j\le n\) 。
- \(1\le a_{i,j}\le 10^5\) 。
时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
记第 \(j\) 天统治党的权力为 \(mx_j\) 。
\(\texttt{Key observation}\) : \(mx_j\) 唯一确定!
证明:
- 如果 \(p_j\lt p_{j-1}\) ,那么 \(mx_j=mx_{j-1}\) ,并且第 \(j\) 天必须选择 \(p_j\) 。
- 如果 \(p_j=p_{j-1}\) ,由于第 \(j\) 天不能选择 \(p_j\) ,因此 \(mx_j=mx_{j-1}\) 。
- 如果 \(p_j\gt p_{j-1}\) ,那么 \(mx_j=mx_{j-1}+1\) ,并且第 \(j\) 天必须选择 \(p_j\) 。
接下来是一个简单的有源汇上下界最大费用可行流问题。
给第 \(j\) 天第 \(i\) 个政党开一个点 \(x_{j,i}\) 。
对于第 \(j\) 天,前 \(p_j\) 个点权力上限为 \(mx_j-1\) ,后 \(n-p_j\) 个点上限为 \(mx_j\) 。
从 \(x_{j,i}\) 向 \(x_{j+1,i}\) 连一条下界为零,上界为权力上限,费用为零的边,以下两种情况除外:
- \(i=p_j\) ,为保证统治党为 \(p_j\) ,将下界改为和上界相等(强制满流)。
- \(j=m\) ,则这条边应连向汇点 \(t\) 。
接下处理收益的问题。
从源点 \(s\) 向虚点 \(y_j\) 连一条上下界均为 \(1\) ,费用为零的边,表示第 \(j\) 天必须恰好选择一个政党。再连边 \((y_j,x_{j,i},0,1,a_{i,j})\) 即可。
时间复杂度 \(\mathcal O(n^2m)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005,maxm=2e4+5,inf=1e9;
int m,n,s,t;
int p[55],mx[55],pos[55],a[55][55];
namespace flow
{
int s,t,mxf,mnc,tot=1;
int head[maxn],to[maxm],f[maxm],val[maxm],nxt[maxm];
int d[maxn],mn[maxn],pre[maxn];
bool vis[maxn];
int all,h[maxn];
void addedge(int u,int v,int c,int w)
{
nxt[++tot]=head[u],to[tot]=v,f[tot]=c,val[tot]=w,head[u]=tot;
nxt[++tot]=head[v],to[tot]=u,f[tot]=0,val[tot]=-w,head[v]=tot;
}
void add(int u,int v,int l,int r,int w)
{
addedge(u,v,r-l,w),h[u]-=l,h[v]+=l,mnc+=l*w;
}
bool spfa()
{
queue<int> q;
memset(d,0xcf,sizeof(d));
memset(mn,0,sizeof(mn));
d[s]=0,mn[s]=inf,q.push(s);
while(!q.empty())
{
int u=q.front();
vis[u]=0,q.pop();
for(int i=head[u];i;i=nxt[i])
{
int v=to[i],w=val[i];
if(f[i]&&d[v]<d[u]+w)
{
d[v]=d[u]+w,mn[v]=min(mn[u],f[i]),pre[v]=i;
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
return mn[t];
}
int ek()
{
addedge(::t,::s,inf,0);
for(int i=1;i<t;i++) h[i]>0?all+=h[i],addedge(s,i,h[i],0):addedge(i,t,-h[i],0);
while(spfa())
{
mxf+=mn[t],mnc+=d[t]*mn[t];
for(int i=t;i!=s;i=to[pre[i]^1]) f[pre[i]]-=mn[t],f[pre[i]^1]+=mn[t];
}
return mxf==all?mnc:-1;
}
}
int id(int x,int y)
{
return (x-1)*n+y;
}
int main()
{
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
scanf("%d%d",&n,&m);
for(int j=1;j<=m;j++) scanf("%d",&p[j]),mx[j]=mx[j-1]+(p[j]>p[j-1]);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
s=n*m+m+1,t=s+1,flow::s=0,flow::t=t+1;
for(int j=1;j<=m;j++)
{
for(int i=1;i<=n;i++) flow::add(id(j,i),j!=m?id(j+1,i):t,i==p[j]?mx[j]:0,mx[j]-(i<p[j]),0);
flow::add(s,n*m+j,1,1,0),pos[j]=flow::tot;
for(int i=1;i<=n;i++) flow::add(n*m+j,id(j,i),0,1,a[i][j]);
}
if(flow::ek()==-1) printf("-1\n"),exit(0);
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
if(flow::f[pos[j]+2*i]) printf("%d%c",i," \n"[j==m]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/18706798
浙公网安备 33010602011771号