cf1208G Polygons

链接

cf
给你两个正整数 n n n k k k,询问在一个圆上你最少需要几个点构才能造出 k k k个边数小于等于 n n n的正多边形

思路

深受迫害,所以写的详细一点,不会请留言。

性质1

考虑加进一个 x x x边形。那么他的因子 d d d一定在他之前加进来了.
因为 d d d可以完全由 x x x的点表现出来。
如果没加 d d d,那么加 d d d显然比加 x x x优秀(显然)。

性质2

两个图形,让他们尽量多的重合些点是好的。
两个图形让他们一个点重合,即可得到最好的。
因为是正多边形,所以随便重合一个点,重合的情况都是一样的。
所以两个正多边形要不‘重合’,要不 ‘不重合’,而且重合的情况都是一样的。
所以我们加入的 k k k个正多边形都重合到一个点上,设这个点为 0 0 0点。

联系起来

x x x在圆上,假设他的点为 0 x , 1 x … … x − 1 x \frac{0}{x},\frac{1}{x}……\frac{x-1}{x} x0,x1xx1
p a r t 2 part2 part2可以知道,0这个点上每个图形都会经过。
p a r t 1 part1 part1可以知道 x x x的点上,他的因子在之前就会加入,所以他的因子及其倍数都是原先就有的(被覆盖过)。
这个过程就是类似于暴力筛 p h i phi phi的过程,所以剩下的就是与他互质的数。
所以一个正 x x x边形的贡献就是 p h i ( x ) phi(x) phi(x).
找出 k k k个最小的 p h i phi phi就行了
其实这个题就是俄罗斯数学竞赛的题目…我同桌给我讲过类似的证明,忘记了(菜)。

代码

因为1,2不是正x边形,所以不能选为k边形,但还是有贡献的。

#include 
using namespace std;
const int _=1e6+7,limit=1e6;
int phi[_];
void Euler() {
	for(int i=1;i<=limit;++i) phi[i]=i;
	for(int i=2;i<=limit;++i) {
		if(phi[i]==i) {
			phi[i]=i-1;
			for(int j=i+i;j<=limit;j+=i)
				phi[j]=(phi[j]/i)*(i-1);
		}
	}
}
std::vector<int> ans;
int main() {
	Euler();
	int n,k;
	cin>>n>>k;
	if(k==1) return puts("3"),0;
	for(int i=3;i<=n;++i) ans.push_back(phi[i]);
	sort(ans.begin(),ans.end());
	long long tot=0;
	for(int i=0;i<k;++i) tot+=ans[i];
	cout<<tot+2<<"\n";
	return 0;
}

你可能感兴趣的:(cf1208G Polygons)