bzoj 3308: 九月的咖啡店(最大费用最大流)

3308: 九月的咖啡店

Time Limit: 30 Sec   Memory Limit: 128 MB
Submit: 278   Solved: 100
[ Submit][ Status][ Discuss]

Description

深绘里在九份开了一家咖啡让,如何调配咖啡民了她每天的头等大事
我们假设她有N种原料,第i种原料编号为i,调配一杯咖啡则需要在这
里若干种兑在一起。不过有些原料不能同时在一杯中,如果两个编号
为i,j的原料,当且仅当i与j互质时,才能兑在同一杯中。
现在想知道,如果用这N种原料来调同一杯咖啡,使用的原料编号之和
最大可为多少。

Input

一个数字N

Output

如题

Sample Input

10

Sample Output

30


zkw网络流不会写,强行加了个SPFA,感觉更慢了

这题很难想到呀,一开始以为就是所有只含有一个质因子且尽可能大的数加在一起,

比如n=200000的时候,找到所有小于200000的质数并将它们一个一个不停地次方直到无限接近于n然后加在一起就好了,但这样不一定最大,有可能可以包含两个质因子!而现实就是,能达到最优的配料编号一定最多只含有两个质因子,且一个小于sqrt(n),另一个大于sqrt(n)

既然这样还是好办

因为小于sqrt(n)的个数非常少,不超过100个,那么可以建张图:

所有小于sqrt(n)的质数与源点连接一条流量为1,费用为0的边

所有大于sqrt(n)的质数与汇点连接一条流量为1,费用为0的边

所有小于sqrt(n)的质数和大于sqrt(n)的质数连接一条流量为1,费用为Vab-Va-Vb的边

其中Va = a^(lgn/lga),Vb = b,Vab = a^(lg(n/b)/lga)*b

如果这个费用小于0则直接不连边就好,最后答案就是最大费用最大流+①情况的值


#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
#define LL long long
typedef struct Res
{
	int next;
	int to, from;
	int flow, cost;
}Road;
Road G[2000005];
int head[200010], vis[200010], dis[200010], pri[200010], flag[200010] = {1,1}, S, T, cnt, n, ans; 
void Add(int u, int v, int flow, int cost)
{
	cnt++;
	G[cnt].next = head[u];
	head[u] = cnt;
	G[cnt].from = u;
	G[cnt].to = v;
	G[cnt].flow = flow;
	G[cnt].cost = cost;
}
int SPFA()
{
	int now, i, v;
	queue<int> q;
	memset(vis, 0, sizeof(vis));
	memset(dis, -62, sizeof(dis));					//如果是求最小费用,初始化应该为很大的正数,下同
	q.push(S);
	vis[S] = 1;
	dis[S] = 0;
	while(q.empty()==0)
	{
		now = q.front();
		q.pop();
		vis[now] = 0;
		for(i=head[now];i!=0;i=G[i].next)
		{
			v = G[i].to;
			if(G[i].flow && dis[v]<dis[now]+G[i].cost)				//如果是求最小费用,这里改成dis[v]>dis[now]+G[i].cost
			{
				dis[v] = dis[now]+G[i].cost;
				if(vis[v]==0)
				{
					vis[v] = 1,
					q.push(v);
				}
			}
		}
	}
	if(dis[T]>0)				//如果是求最小费用,这里改成dis[T]<inf(inf为一个很大的正数)
		return 1;
	return 0;
}
int Sech(int now, int low)
{
	int i, w, used;
	vis[now] = 1;
	if(now==T)
		return low;
	used = low;
	for(i=head[now];i!=0;i=G[i].next)
	{
		if(G[i].flow && dis[G[i].to]==dis[now]+G[i].cost && vis[G[i].to]==0)
		{
			w = Sech(G[i].to, max(used, G[i].flow));				//如果是求最小费用,这里改成min
			G[i].flow -= w;
			G[i^1].flow += w;
			used -= w;
			ans += w*G[i].cost;
			if(used==0)
				return low;
		}
	}
	return low-used; 
}
int Jud(int up, int x)
{
	LL t = 1;
	while(t*x<=up)
		t = t*x;
	return t;
}
int main(void)
{
	int i, j, pos, temp, sum;
	scanf("%d", &n);
	sum = 0;
	for(i=2;i<=n;i++) 
	{
		if(flag[i])
			continue;
		pri[++sum] = i;
		for(j=i*2;j<=n;j+=i)
			flag[j] = 1;
	}
	cnt = 1;
	pos = 0;
	S = 0, T = sum+1;
	for(i=1;i<=sum;i++)
	{
		if(pri[i]>=n/2)
		{
			ans += pri[i];
			continue;
		}
		if((LL)pri[i]*pri[i]<=n)
		{
			Add(S, i, 1, 0);
			Add(i, S, 0, 0);
			ans += Jud(n, pri[i]);
			pos = i;
		}
		else
		{
			Add(i, T, 1, 0);
			Add(T, i, 0, 0);
			ans += pri[i];
		}
	}
	for(i=1;i<=pos;i++)
	{
		for(j=pos+1;j<=sum;j++)
		{
			if((LL)pri[i]*pri[j]>n)
				break;
			temp = Jud(n/pri[j], pri[i])*pri[j]-Jud(n, pri[i])-pri[j];
			if(temp>0)
			{
				Add(i, j, 1, temp);
				Add(j, i, 0, -temp);
			}
		}
	} 
	while(SPFA()) 
	{
		memset(vis, 0, sizeof(vis));
		while(Sech(S, -1000000000))
			memset(vis, 0, sizeof(vis));
	}
	printf("%d\n", ans+1);
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值