【2019雅礼集训】【最大费用流】【模型转换】D2T3 sum
题意
现在你有一个集合{1,2,3,...,n},要求你从中取出一些元素,使得这些元素两两互质。问你能够取出的元素总和最多是多少?
输入格式
一个整数n
输出格式
一个整数表示能够选出的最大的元素总和。
思路
这道题居然是结论+网络流?根本想不到啊...~~
感觉写题解都只能照搬官方题解了~~
首先出题人就给了两个自己也没有证出来的结论:
- 最后选出的数至多只有两个不同的质因子
- 每个选出的数val所包含的质因子如果有两个的话,一定是一个小于\(\sqrt{val}\),还有一个是大于\(\sqrt{val}\)。
至于为什么呢?好像没有人知道...
然后我们考虑建图。如果暴力建图(就是像二分图那样),将所有的质因子分为两个部,左边是小于等于\(\sqrt {n}\)的,右边是大于\(\sqrt {n}\),然后两个部之间两两连边就可以了。但是这样子边的数量好像多了一点..."据说"可能会T(没试过)。
那么就是优化。
首先,先定义F(pri[1],pri[2]...pri[k])为包含pri[]这些质因子的最大的小于等于n的数。然后我们能够发现,F(pri[1]),F(pri[2]),F(pri[3])...F(pri[pri.size()-1])这些数字是比较优秀的。
然后由于分成了两个部,我们考虑左边一个数质数x,右边一个数质数y,如果我们加入F(x,y)的话,我们能够发现F(x)和F(y)就不能选了。那么就会获得\(F(x,y)-F(x)-F(y)\)的贡献。只有当这个值大于0的时候,我们才将这条边加进去。
宏观感受下,这样子之后边的数量就变得很少了...然后你可以过了...
建图的话,就是源点S连向左边的部,容量为1,费用为0;
左边的部按照上述方式向右边的部连边,容量为1,费用为\(F(x,y)-F(x)-F(y)\);
右边的部向汇点T连边,容量为1,费用为0。
然后跑一个最大费用流(注意不是最大流!!wa了好久!!注意当当前求出来的费用小于0时需要退出!!否则答案就会偏小),在加上最开始的初始值(\(\sum_{i=1}^{k}F(pri[i])\)),就是最后的答案了。
代码
我的实现是建立负权边跑最小费用流,可能具体细节和上述的做法是反着的。如果你采用SPFA求最长路的话,细节和上面就是一样的了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define MAXN 200000
#define INF 1000000000000000000LL
using namespace std;
typedef long long LL;
struct node
{
int to;
LL cap,cost;
node *nxt,*bck;
}edges[MAXN*10+5];
node *ncnt=&edges[0],*Adj[MAXN+5];
int S,T,n;
vector<LL> prime,p1,p2;
bool vis[MAXN+5],inque[MAXN+5];
LL maxval[MAXN+5],dist[MAXN+5],d[MAXN+5];
int prevv[MAXN+5];
node *preve[MAXN+5];
queue<int> que;
void Sieve()
{
for(int i=2;i<=n;i++)
{
if(vis[i]==false)
prime.push_back(i);
for(int j=0;1LL*prime[j]*i<=n;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
break;
}
}
}
LL F(LL x,LL y)
{
LL ret=y;//由于y>sqrt(n),所以y这个质因子在ret中只会出现一次
while(1LL*ret*x<=n)
ret*=x;
return ret;
}
void AddEdge(int u,int v,LL cap,LL cost)
{
node *p=++ncnt;
p->to=v;p->cap=cap;p->cost=cost;
p->nxt=Adj[u];Adj[u]=p;
node *q=++ncnt;
q->to=u;q->cap=0;q->cost=-cost;
q->nxt=Adj[v];Adj[v]=q;
p->bck=q;q->bck=p;
}
void Build()
{
S=0,T=(int)prime.size()+1;
for(int i=0;i<(int)prime.size();i++)
if(1LL*prime[i]*prime[i]<=n)
AddEdge(S,i+1,1,0),p1.push_back(i);
else
AddEdge(i+1,T,1,0),p2.push_back(i);
for(int i=0;i<(int)p1.size();i++)
for(int j=0;j<(int)p2.size();j++)
{
LL num1=prime[p1[i]],num2=prime[p2[j]];
LL num=F(num1,num2);
if(num-maxval[p1[i]]-maxval[p2[j]]>0)
{
// printf("val:%lld\n",num-maxval[p1[i]]-maxval[p2[j]]);
AddEdge(p1[i]+1,p2[j]+1,1,-num+maxval[p1[i]]+maxval[p2[j]]);
}
}
}
void SPFA()
{
while(que.empty()==false)
que.pop();
memset(inque,0,sizeof(inque));
fill(dist,dist+T+1,INF);
fill(d,d+T+1,INF);
dist[S]=0;
que.push(S);inque[S]=true;
while(que.empty()==false)
{
int u=que.front();
que.pop();inque[u]=false;
for(node *p=Adj[u];p!=NULL;p=p->nxt)
{
int v=p->to;
LL w=p->cost;
if(p->cap&&dist[v]>dist[u]+w)
{
if(inque[v]==false)
inque[v]=true,que.push(v);
d[v]=min(d[u],p->cap);
dist[v]=dist[u]+w;
prevv[v]=u;preve[v]=p;
}
}
}
}
LL Min_Cost_Flow()
{
LL flow=0,cost=0,delta;
int u,v;
while(true)
{
SPFA();
if(d[T]==INF||dist[T]>=0)//就是这里,一定要判一下!!!否则答案偏小
break;
delta=d[T];
for(v=T;v!=S;v=u)
{
u=prevv[v];
node *&p=preve[v];
p->cap-=delta;
p->bck->cap+=delta;
cost+=1LL*p->cost*delta;
}
}
return cost;
}
int main()
{
scanf("%d",&n);
Sieve();
LL ans=1LL;
for(int i=0;i<(int)prime.size();i++)
maxval[i]=F(prime[i],1LL),ans+=maxval[i];//先确定初始的值
// printf("%lld\n",ans);
Build();
ans-=min(0LL,Min_Cost_Flow());//加上可能存在的更有的方案(应该可以不用取min,懒得改了...)
printf("%lld\n",ans);
return 0;
}
【2019雅礼集训】【最大费用流】【模型转换】D2T3 sum的更多相关文章
- 【2019雅礼集训】【可持久化线段树】【模型转化】D1T2Permutation
目录 题意 输入格式 输出格式 思路 代码 题意 给定一个长度为n的序列A[],你需要确定一个长度为n的排列P[],定义当前排列的值为: \[\sum_{i=1}^{n}{A[i]P[i]}\] 现在 ...
- 【2019雅礼集训】【CF 960G】【第一类斯特林数】【NTT&多项式】permutation
目录 题意 输入格式 输出格式 思路 代码 题意 找有多少个长度为n的排列,使得从左往右数,有a个元素比之前的所有数字都大,从右往左数,有b个元素比之后的所有数字都大. n<=2*10^5,a, ...
- 雅礼集训2019 D7T2 Subsequence
雅礼集训2019 D7T2 Subsequence 直接贴题解: 平衡树代码: #include<bits/stdc++.h> #define ll long long #define N ...
- LOJ_6045_「雅礼集训 2017 Day8」价 _最小割
LOJ_6045_「雅礼集训 2017 Day8」价 _最小割 描述: 有$n$种减肥药,$n$种药材,每种减肥药有一些对应的药材和一个收益. 假设选择吃下$K$种减肥药,那么需要这$K$种减肥药包含 ...
- 雅礼集训【Day6-1】字符串
雅礼集训[Day6-1]字符串 假设我们有串\(a\),我们设\(a'\)为\(a\)翻转后按为取反过后的串. 我们只考虑前一半的,长为\(m\)的串.如果前半截匹配了\(a\)或者\(a'\),则\ ...
- 「雅礼集训 2017 Day7」事情的相似度
「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...
- 「雅礼集训 2017 Day2」解题报告
「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...
- 「雅礼集训 2017 Day1」 解题报告
「雅礼集训 2017 Day1」市场 挺神仙的一题.涉及区间加.区间除.区间最小值和区间和.虽然标算就是暴力,但是复杂度是有保证的. 我们知道如果线段树上的一个结点,\(max=min\) 或者 \( ...
- [LOJ 6031]「雅礼集训 2017 Day1」字符串
[LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...
随机推荐
- vue的计算属性与方法的不同
计算属性 vue的模板里可以使用表达式,但是它的设计初衷是用于简单计算,在模板中放入太多逻辑会让模板过重且难以维护.例如: <div id="example"> {{ ...
- 非阻塞读和写:str_cli函数
void str_cli(FILE *fp, int sockfd) { int maxfdp1, val, stdineof; ssize_t n, nwritten; fd_set rset, w ...
- JS数组(JSON)整合篇-方法整理
遍历:arr_Param.forEach(function (item, i) {}); 反序排序:arr_Param.reverse(); 合并数组:arr_Param.push.apply(arr ...
- BUAA-OO-电梯调度
BUAA-OO-电梯调度 1.设计策略 需求分析 设计一个系统,使其可以根据乘客的当前楼层和目的楼层,为乘客分配电梯资源并运送其至目的楼层. 自顶向下 根据需求,可以将整个系统分成三个部分: 处理乘客 ...
- httpClient closeableHttpClient
https://www.cnblogs.com/lyy-2016/p/6388663.html
- class和style属性
值类型: 1.表达式计算出的字符串结果或者字符串. 2.对象(表达式或者对象名,建议采用对象名) key为className,值的真假控制这个name的有无. //class <div v-bi ...
- mysql 8.0~MGR多成员读一致性
一 背景:当在读节点多成员查询时可能导致数据不一致 二 三种场景 1 读多写少 AFTER 2 读写相当 AFTER_AND_BEFORE 3 读少写多 BEFORE三 数据不一致 ...
- ruban后台项目启动。进入断点
eclipse debug启动 进入断点显示-------------HikariPool-1 - Starting... 原因是 eclipse和tomcat的交互而产生的,在以debug模式启动t ...
- 【easy】278. First Bad Version
有一系列产品,从某个开始其后都不合格,给定一个判断是否合格的函数,找出N个产品中第一个不合格的产品. 正确答案: // Forward declaration of isBadVersion API. ...
- springboot 多模块打war 部署
先展示一下自己项目结构 一共有5个模块 依赖关系:下面的模块依赖上面所有的模块 其中 rongke-web是我要部署的模块 最终要打war进行部署,其他模块均打jar 被rongke-web引用. 开 ...