LOJ3400 「2020-2021 集训队作业」Storm 题解
题目描述
\(T\) 组数据,给定一张 \(n\) 个点, \(m\) 条边的无向图,点权为 \(v_i\) ,边权为 \(e_i\) 。
求一个大小不超过 \(k\) 的边集 \(S\) (可以为空),要求最大化:
其中 \(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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17362554.html
浙公网安备 33010602011771号