peiwenjun's blog 没有知识的荒原

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;
}

posted on 2025-02-09 22:03  peiwenjun  阅读(24)  评论(0)    收藏  举报

导航