#NOIP模拟赛#吃糖果candy(缩小选边范围--mod区间)
来源:互联网 发布:shell脚本与python 编辑:程序博客网 时间:2024/06/03 09:10
吃糖果(candy)
【题目描述】
小D有一包糖果和N张卡片。每张卡片上都有一个正整数Pi。小D想这样吃糖果,他把两张卡片用线串起来,如果两张卡片上的数字分别为Pa和Pb,他就吃掉min(Pa%Pb,Pb%Pa)的糖果。
他想最终把所有的卡片都串起来——即提起一张卡片,就可以将其他卡片都提起。请问他最少需要吃掉多少糖果。
【输入格式】
第一行包含一个正整数N(1<=N<=105)
接下来N行,每行一个正整数Pi(1<=Pi<=107)
【输出格式】
有且只有一行,输出答案。
【数据范围】
30%的数据N<=103
40%的数据Pi<=106
70%的数据上述条件至少满足1个。
【输入样例1】
4
2
6
3
11
【输出样例1】
1
【输入样例2】
4
1
2
3
4
【输出样例2】
0
【输入样例3】
3
4
9
15
【输出样例3】
4
对于两个点u,v它们边的长度就是min(Pu%Pv,Pv%Pu),
如果两张卡片上的数相等,那么他们之间边权为0,可以将他们看做一个顶点。
于是我们只需要考虑卡片上的互不相等的情况。
设卡片的最大值为maxz,注意到maxz不超过10^7,将卡片按值由小到大排序,对于某张卡片i,设它的值为pi。
以pi为周期,将[pi,maxz]分为若干个区间,最后一个区间可能不足pi。
在每个区间找到模pi的值最小的卡片,将该卡片和卡片i连边,特别的,第一个区间即[pi,2*pi-1],要注意不能找第i张卡片本身。
我们只需要在刚才连的边中求一个最小生成树即可。
aft[P[i]]表示值大于P[i]的第一个值。
下面给出一些思考点和证明:
1,为什么第一段要单独处理?
因为每段区间mod最小的值应该储存在aft[P[i] * x]的地方,但是第一段不能选P[i],所以必须向后移一个成为aft[P[i] + 1]
2,为什么每个P[i]值只对它处理一遍?
因为V的下标是边的长度,而每个相同的点权之间可以连边使得边权为0,所以只处理一遍,就是将那一堆权值相同的点中选出一个和其他权值不同的点相连。
3,区间中的点为什么选择aft边界位置中的最优?
因为对于P[i]来说,倍数肯定是mod值最小的,而不是倍数的话,就考虑离倍数最近的一个为当前区间中的最优值。
4,为什么从选出来的这些边中做最小生成树一定能保证N个点都连通?
注意我们的循环是对于每一个i而言的,所有点权不同的i一定都会被枚举到,并且每个点i都至少会在大小为P[i]的区间中找到一个与之相匹配的点来连边,最后的并查集一定会访问到V数组中所有的点,并保证将它们连成一棵最小生成树。
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<vector>#include<cassert>#include<algorithm>using namespace std;const int Max = 1 << 19;const int Maxm = 1e7;struct node{ int u, v; node(){} node(int a, int b){u = a, v = b;}};int N;int P[Max + 5], fa[Max + 5], rnk[Max + 5];int pos[Maxm + 5], aft[Maxm + 5], Cnt[Maxm + 5];vector<node>V[Maxm + 5];bool getint(int & num){ char c; int flg = 1; num = 0; while((c = getchar()) < '0' || c > '9'){ if(c == '-') flg = -1; if(c == -1) return 0; } while(c >= '0' && c <= '9'){ num = num * 10 + c - 48; if((c = getchar()) == -1) return 0; } num *= flg; return 1;}int root(int x){ if(! fa[x]) return x; return fa[x] = root(fa[x]);}int main(){ freopen("candy.in", "r", stdin); freopen("candy.out", "w", stdout); getint(N); for(int i = 1; i <= N; ++ i){ getint(P[i]); aft[P[i]] = P[i]; if(pos[P[i]] == 0) pos[P[i]] = i; } for(int i = Maxm - 1; i >= 0; -- i) if(! aft[i]) aft[i] = aft[i + 1]; for(int i = 1; i <= N; ++ i){ if(Cnt[P[i]] ++) continue; if(aft[P[i] + 1]){ if(P[i] * 2 > Maxm || aft[2 * P[i]] != aft[P[i] + 1]) V[aft[P[i] + 1] - P[i]].push_back(node(i, pos[aft[P[i] + 1]])); } for(int j = 2 * P[i]; j <= Maxm && aft[j]; j += P[i]) if(j + P[i] > Maxm || aft[j + P[i]] != aft[j]) V[aft[j] - j].push_back(node(i, pos[aft[j]])); } long long rt = 0LL; for(int i = 0; i <= Maxm; ++ i) for(int j = 0; j < V[i].size(); ++ j){ int a = root(V[i][j].u), b = root(V[i][j].v); if(a != b){ if(rnk[a] > rnk[b]) fa[b] = a; else if(rnk[a] < rnk[b]) fa[a] = b; else fa[a] = b, ++ rnk[a]; rt += i; } } printf("%lld\n", rt); return 0;}
- #NOIP模拟赛#吃糖果candy(缩小选边范围--mod区间)
- [NOIP模拟赛]吃糖果
- HDOJ1205(吃糖果)
- NOIP模拟(11.03)T1 区间
- [NOIP模拟] 区间
- LeetCode 135. Candy(糖果)
- hdu(1205)吃糖果
- 吃糖果(组合数学)
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- 吃糖果
- PAT练习题002
- 使用json-lib将Java对象转json死循环问题
- git
- 重新编写IT笔记
- dfs+数学? 单
- #NOIP模拟赛#吃糖果candy(缩小选边范围--mod区间)
- AppiumLibrary常用关键字
- 9.2题解
- virsh 查看cpu架构,以及guest vm numa应分配架构
- hdu6185 Covering (递推+矩阵快速幂)
- 搬瓦工vps+ss+ipv6+校园网免流量上网
- python结巴分词、jieba加载停用词表
- BZOJ 2563: 阿狸和桃子的游戏 贪心 思路题
- shell脚本在容器云中搭建hadoop集群(二)