luoguP4068 [SDOI2016]数字配对
作者:互联网
题意
看见配对数最多,想到这是最大流。看到代价,想到这是最大费用流。
于是这题是最大费用最大流。
做完了,撒花!
我们发现这题没有明显的组别之分,也就是说我们并不知道建图时谁连源点谁连汇点。
再次观察题中给出的配对的条件:\(a_i\)是\(a_j\)的倍数且满足\(\frac{a_i}{a_j}\in prime\),即\(a_i=k*a_j,k\in prime\)。
设\(cnt_x\)表示\(x\)质因数分解后指数之和,如\(cnt_{12}=3\),因为\(12=3^1*2^2,1+2=3\)。
我们发现题中条件变为:\(a_i\)是\(a_j\)的倍数且满足\(cnt_{a_i}=cnt_{a_j}+1!!!\)
于是我们可以按照\(cnt_{a_i}\)的奇偶性对\(i\)分类,奇数连源点,偶数连汇点,容量都为\(b_i\),费用为\(0\)。
之后就枚举每对\((i,j)\),满足条件就从\(cnt\)为奇数的向另一个连容量为\(b_i*b_j\),费用为\(c_i*c_j\)的边即可。
如果没有这个限制题就已经做完了:“在获得的价值总和不小于\(0\)的前提下”
为了处理这个限制,我们考虑\(Dinic\)的过程,我们每次\(spfa\)的求出的最长路必定是递减的,因为这是\(Dinic\)复杂度的保证。(可以去看\(Dinic\)的复杂度证明,就是用最长/短路总是变得更差证的)
于是我们每次求出费用后就判断下加上是否会小于\(0\),不小于\(0\)就直接加上,不然就只加加上刚好不小于\(0\)的那一部分,之后\(break\)掉。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=210;
const ll inf=1e18;
int n,cnt_edge=1,S,T;
int a[maxn],b[maxn],c[maxn],head[maxn],cnt[maxn];
ll dis[maxn];
bool vis_prime[100010],vis[maxn];
vector<int>prime;
struct edge{int to,nxt;ll flow,cost;}e[(maxn*maxn)<<2];
inline ll read()
{
char c=getchar();ll res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline void add(int u,int v,ll w,ll c)
{
e[++cnt_edge].nxt=head[u];
head[u]=cnt_edge;
e[cnt_edge].to=v;
e[cnt_edge].flow=w;
e[cnt_edge].cost=c;
}
inline void addflow(int u,int v,ll w,ll c){add(u,v,w,c);add(v,u,0,-c);}
inline void pre_work()
{
vis[1]=1;
for(int i=2;i<=100000;i++)
{
if(!vis_prime[i])prime.push_back(i);
for(unsigned int j=0;j<prime.size()&&i*prime[j]<=100000;j++)
{
vis_prime[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
inline bool spfa()
{
memset(vis,0,sizeof(vis));
for(int i=S;i<=T;i++)dis[i]=-inf;
queue<int>q;
q.push(S);dis[S]=0;vis[S]=1;
while(!q.empty())
{
int x=q.front();q.pop();vis[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dis[y]<dis[x]+e[i].cost&&e[i].flow>0)
{
dis[y]=dis[x]+e[i].cost;
if(!vis[y])q.push(y),vis[y]=1;
}
}
}
return dis[T]!=-inf;
}
ll dfs(int x,ll lim)
{
vis[x]=1;
if(x==T||lim<=0)return lim;
ll res=lim;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dis[y]!=dis[x]+e[i].cost||e[i].flow<=0||vis[y])continue;
ll tmp=dfs(y,min(res,e[i].flow));
res-=tmp;
e[i].flow-=tmp;e[i^1].flow+=tmp;
if(res<=0)break;
}
return lim-res;
}
inline ll Dinic()
{
ll res=0,cost=0;
while(spfa())
{
ll flow=dfs(S,inf);
if(cost+flow*dis[T]>=0)res+=flow,cost+=flow*dis[T];
else
{
res+=min(flow,cost/(-dis[T]));
break;
}
}
return res;
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
pre_work();
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
for(int i=1;i<=n;i++)c[i]=read();
for(int i=1;i<=n;i++)
{
int tmp=a[i];
for(unsigned int j=0;j<prime.size()&&1ll*prime[j]*prime[j]<=tmp;j++)
while(tmp%prime[j]==0)cnt[i]++,tmp/=prime[j];
if(tmp>1)cnt[i]++;
}
S=0,T=n+1;
for(int i=1;i<=n;i++)
if(cnt[i]&1)addflow(S,i,b[i],0);
else addflow(i,T,b[i],0);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
int x=a[i],y=a[j];
if(((x%y!=0)&&(y%x!=0))||abs(cnt[i]-cnt[j])!=1)continue;
x=i,y=j;
if(cnt[y]&1)swap(x,y);
addflow(x,y,1ll*b[x]*b[y],1ll*c[x]*c[y]);
}
printf("%lld",Dinic());
return 0;
}
标签:cnt,int,ll,vis,maxn,SDOI2016,luoguP4068,配对,dis 来源: https://www.cnblogs.com/nofind/p/12109835.html