peiwenjun's blog 没有知识的荒原

LOJ3400 「2020-2021 集训队作业」Storm 题解

题目描述

\(T\) 组数据,给定一张 \(n\) 个点, \(m\) 条边的无向图,点权为 \(v_i\) ,边权为 \(e_i\)

求一个大小不超过 \(k\) 的边集 \(S\) (可以为空),要求最大化:

\[\sum_{x\in N(S)}v_x-\sum_{y\in S}e_y\\ \]

其中 \(N(S)\) 为由 \(S\) 的所有端点组成的集合,输出最大值。

数据范围

  • \(1\le T\le 5,1\le n,m\le 5\cdot 10^5,1\le k\lt 20,1\le\sum 2^k(n+m)\le 10^6\)
  • \(0\le e_i,v_i\le 10^8\)
  • 图中无自环,但可能有重边,保证答案 \(\le 2\cdot 10^9\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\)

分析

显然 \(S\) 不会构成环,因此 \(S\) 为森林。

给每个点随机分配黑色或白色,再从二分图的角度考虑这个问题。

从源点向每个黑点连边 \((1,v_x),(\infty,0)\) ,从每个白点向汇点连边 \((1,v_x),(\infty,0)\) ,中间黑点向白点连边 \((1,-e_y)\) ,跑限制流量 \(\le k\) 的最大费用流即可。

计算一下正确的概率。

对于最优方案中的每条边,有 \(\frac 12\) 的概率让它的端点不同色,因此每一轮正确的概率为 \(\frac 1{2^k}\)

注意到 \((1-\frac 1x)^x\le\frac 1e\) ,因此跑 \(10\cdot 2^k\) 轮,错误概率为 \((1-\frac 1{2^k})^{10\cdot 2^k}\le\frac 1{e^{10}}\approx 4.5\cdot 10^{-5}\)

再来分析时间复杂度。

对于每一次费用流,我们只需要跑 \(k\) 条流量,注意到二分图中走反向边相当于反悔了一组匹配,而跑第 \(i\) 条流量之前,我们只有不超过 \(i-1\) 条边用于反悔,因此第 \(i\)SPFA 的代价为 \(\mathcal O(i\cdot(n+m))\)

时间复杂度 \(\mathcal O(10\cdot 2^k\cdot k^2(n+m))\) ,可以通过本题。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5,maxm=2e6+5,inf=1e9;
int k,m,n,t,res;
int a[maxn],col[maxn];
int u[maxn],v[maxn],w[maxn];
mt19937 rnd(time(0));
namespace flow
{
    int s,t,tot;
    int head[maxn],to[maxm],f[maxm],val[maxm],nxt[maxm];
    int d[maxn],mn[maxn],pre[maxn];
    bool ins[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 clean(int _t)
    {
        s=0,t=_t,tot=1;
        for(int i=s;i<=t;i++) head[i]=0;
    }
    bool spfa()
    {
        queue<int> q;
        for(int i=s;i<=t;i++) d[i]=-inf,mn[i]=0;
        d[s]=0,mn[s]=inf,q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            ins[u]=false,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(!ins[v]) ins[v]=true,q.push(v);
                }
            }
        }
        return mn[t];
    }
    int ek()
    {
        int flow=0,cost=0;
        while(flow<=k&&spfa())
        {
            if(d[t]<=0) break;
            cost+=min(k-flow,mn[t])*d[t],flow+=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 cost;
    }
}
using flow::addedge;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k),res=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=m;i++) scanf("%d%d%d",&u[i],&v[i],&w[i]);
        for(int i=1;i<=10*(1<<k);i++)
        {
            flow::clean(n+1);
            for(int j=1;j<=n;j++)
            {
                static int x=0,y=0;
                col[j]=rnd()&1;
                if(col[j]) x=flow::s,y=j;
                else x=j,y=flow::t;
                addedge(x,y,1,a[j]),addedge(x,y,inf,0);
            }
            for(int j=1;j<=m;j++)
            {
                if(col[u[j]]==col[v[j]]) continue;
                if(!col[u[j]]) swap(u[j],v[j]);
                addedge(u[j],v[j],1,-w[j]);
            }
            res=max(res,flow::ek());
        }
        printf("%d\n",res);
    }
    return 0;
}

posted on 2023-04-28 16:28  peiwenjun  阅读(20)  评论(0)    收藏  举报

导航