牛客挑战赛65 E 费用流

233的物品

出题人钟爱阴间费用流。

看起来很不可做也像是费用流的模型。也有匹配味道。

考虑一个点既在S集合又在T集合相当难以处理。

但是注意到一个点在S集合就一定要有T集合与之匹配不然相当于还是在T集合中。

这样我们知道S集合里的数大小不超过1000

但是这样我们还是难以建图。

阴间出题人。

爆搜得到只有150,294这两个数字满足既在S又在T中。

暴力枚举4种状态就变成了每个数字只能在S和T中。

再考虑费用的建立,因为这个匹配可以多对1的进行匹配每次费用不好算。

但实际上对于T中一个点多次选取费用递减所以将其费用的边全部列出来这样可以保证选取的连续性。

这一点非常的妙。将边权设为选取过后答案减小的量 跑最小费用最大流即可。

由于流量从1到n 所以逐次加流即可,当然直接EK比zkw快不少。

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 5000000000000000ll
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define l(w) t[w].l
#define r(w) t[w].r
#define m(w) t[w].m
#define mn(w) t[w].mn
#define c(w) t[w].c
#define s(w) t[w].s
#define tag(w) t[w].tag
#define S second
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define zz p<<1
#define yy p<<1|1
using namespace std;
const int MAXN=510,maxn=MAXN*MAXN<<1;
int n,len,S,T,SS;
int op[MAXN],vis[MAXN];
int a[MAXN],b[MAXN],c[MAXN],d[MAXN],in[MAXN],pre[MAXN];
ll ans[MAXN],dis[MAXN],res;
int lin[MAXN],ver[maxn],nex[maxn],e[maxn];ll e1[maxn];
inline void add(int x,int y,int z,ll z1)
{
	ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;e1[len]=z1;
	ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0;e1[len]=-z1;
}
inline int pd(int x)
{
	if(x==0||x==1)return 0;
	for(int i=2;i*i<=x;++i)if(x%i==0)return 0;
	return 1;
}
int q[maxn];
inline int spfa()
{
	rep(1,T,i)dis[i]=INF;
	int l=0,r=0;
	q[++r]=S;dis[S]=0;in[S]=520;
	while(++l<=r)
	{
		int x=q[l];vis[x]=0;
		go(x)
		{
			if(!e[i])continue;
			if(dis[x]+e1[i]<dis[tn])
			{
				dis[tn]=dis[x]+e1[i];
				in[tn]=min(in[x],e[i]);
				pre[tn]=i;
				if(!vis[tn])q[++r]=tn,vis[tn]=1;
			}
		}
	}
	return dis[T]!=INF;
}
inline void EK()
{
	int x=T,i;
	res+=dis[T]*in[T];
	while(x!=S)
	{
		i=pre[x];
		e[i]-=in[T];
		e[i^1]+=in[T];
		x=ver[i^1];
	}
}
inline void solve(int x)
{
	len=1;ll cc=0;res=0;
	rep(1,T,i)
	{
		b[i]=d[i];
		if(a[i]==150)op[i]=x&1;
		if(a[i]==294)op[i]=x&2;
		lin[i]=0;
	}
	add(S,SS,0,0);
	int kk=0;
	rep(1,n,i)
	{
		cc+=(ll)b[i]*b[i];
		if(op[i])
		{
			add(SS,i,1,0);
			rep(1,n,j)
			{
				if(!op[j]&&a[j]%((ll)a[i]*a[i]-a[i])==0)
					add(i,j,1,0);
			}
			++kk;
		}
	}
	rep(1,n,i)
	{
		if(!op[i])
		{
			for(int j=1;j<=kk;++j)
			if(b[i]>=c[i])
			{
				add(i,T,1,-((ll)b[i]*b[i]-(ll)(b[i]-c[i])*(b[i]-c[i])));
				b[i]-=c[i];
			}
			else break;
		}
	}
	rep(1,n,i)
	{
		++e[2];
		if(spfa())
		{
			EK();
			ans[i]=min(ans[i],res+cc);
		}
		else break;
	}
}
int main()
{
	freopen("1.in","r",stdin);
	sc(n);S=n+1;SS=S+1;T=SS+1;
	rep(1,n,i)
	{
		sc(a[i]);sc(b[i]);sc(c[i]);d[i]=b[i];
		ans[i]=INF;
		if(a[i]<=1000&&pd(a[i]&(a[i]>>1)))op[i]=1;
	}
	solve(0);solve(1);solve(2);solve(3);
	rep(1,n,i)
	{
		if(ans[i]==INF)puts("-1");
		else putl(ans[i]);
	}
	return 0;
}

一个注意点 T集合连边只需要连S集合大小即可 不需要把所有边连上那样数量级很大。

还没调就过了我很不认同。

posted @ 2022-12-13 15:18  chdy  阅读(28)  评论(0编辑  收藏  举报