poj 3372
直接推还真没推出来。。。看了别人讲的好多也没明白,最后看这个讲的还是不错的。
题意:N个小孩围成一个圈,老师 顺时针隔 0, 1, 2, 3,。。。个小孩发糖,问每个小孩是否都能领到糖。
解法很简单 当N是2^K时输出YES,否则输出NO。printf((N & N - 1) ? "NO\n" : "YES\n");
关键这个怎么证明呢。看到Discuss里说和2次剩余有关,但是没有看出来。。。下面给出自己的简易证明。
当N是奇数时,拿 7 为例。当增量大于N时,就mod N,对增量没有影响。
增量 0 1 2 3 4 5 6 0 1 2 3 4 5 6
编号 0 1 3 6 3 1 0 0 1 3 6 3 1 0
你会看到,这个数列是重复的,重复区间大小是7,再观察着[0, 6]你会发现 这个重复区间是对称的,为什么会这样?
因为 3 + 4 = 7,0 +1 + 2 + 3 + 4的本质就是 0 +1 + 2 所以N为奇数时,他的重复区间都是对称的,所以可知,他最多只能发给 (N + 1) / 2个小孩糖吃,7时为 0 1 3 6 这4个小孩。
所以N为奇数时,一定为NO。
当N是偶数时,又有什么样的景象呢?以 10 为例:
增量 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
编号 0 1 3 6 0 5 1 8 6 5 5 6 8 1 5 0 6 3 1 0
你会发现他是以 2 * N的重复区间出现的。为什么会这样,因为 1 + 9 = 10,2 + 8 = 10。。。
那么 0 + 1 + ....9 % 10 = 5。即 0 + 1 + 。。。N - 1 % N = N / 2。两个区间合起来则又回到了0。
而再次向后加的时候 0 + ... (N - 1) + 0 + 1 = 0 + ...(N - 2) ,和奇数同样的道理,重复区间的内部是对称的。
怎么再次利用这样对称呢,我们发现 出现的编号其实是 从 0 为起点向前走 N / 2步,以及以 N / 2为起点向前走 N / 2步。
在根据编号0,和编号N / 2 在环上的对称性,来解决。
先看 0 的 N / 2步产生的路径L1,是 0 1 3 6 0 ,这里面出来一个大于N / 2的元素 6 。
再看 N / 2 的路径L2, 是 5 6 8 1 5,这里面出现了一个小于N / 2的元素 1。
而6 % 5 = 1。这是由于0和N / 2对称性的关系。
当 0 的 路径L1 中的 位置i的编号j 大于 N / 2,那么 L2中位置i上的编号一定是 j % (N / 2)。
那么我们就可以修改两条路径使其互不侵犯,让L1上所有值都mod (N / 2) ,如果L1能覆盖0 到 N / 2 - 1,则L2即可覆盖 N / 2 到 N - 1。
那么L1本质上就转化成了更小的问题 即 F(N / 2);
写出递归式
| Yes(N == 1);
F(N) = | F(N / 2) (N & 1 == 0)
| NO (N & 1 == 1)
所以可知 N 只有为2^K输出YES。
//============================================================================ // Name : 3372.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <cstdio> #include <cstring> using namespace std; long long n, k; int main() { while(scanf("%lld", &n)!=EOF){ k = n; while(k%2 == 0){ k/=2; } if(k == 1){ printf("YES\n"); } else{ printf("NO\n"); } } return 0; }