Solution [CTSC2001]聪明的学生
题目大意:有三个人,头上分别有三个正整数,最大的数等于最小的两个数之和。每个人只能看到另外两个人头上的数字。假设三人绝顶聪明,从第一个人开始轮流回答是否已经猜出了自己头上的数。给定 (n,m),问所有方案使得前 (n - 1) 轮没人猜出,而在第 (n) 轮,应该回答问题的那个人猜出了自己头上的数字为 (m)。
思维
分析:
样例 1 1 2
和 2 3 1
应该还是比较有提示作用的
首先只要有两个数字相同,那么另一个人就可以猜出自己的数是它们的和(因为没有 (0))
其他情况,我们用 2 3 1
为例,(3) 会在第 (2) 轮猜出答案。
轮到 (3) 时,它发现自己有 (2) 种情况,要么是 (1),要么是 (3)
而如果自己是 (1) 的话,那么 2 1 1
,(2) 会在第 (1) 轮猜出答案。所以自己只能是 (3)。
那么我们设 (f(a,b,c)) 表示三个人的数字,从头开始猜,需要多少轮有人可以猜出来。
转移的话就直接人类智慧讨论即可,每次将最大的数变为另外两个的差的绝对值,边界条件就是有两数相等。
以 (f(a,b,c) quad a geq b geq c) 为例
假设我们已经知道了 (f(b - c,b,c)) 的值,那么它的 (b) 是最大值
而 (f(a,b,c)) 中 (a) 是最大值
所以 (f(a,b,c) = f(b-c,b,c)+2)
其他的看代码就可以了,有两份,注释的是原来写的,没注释的是惨遭卡常之后,参考 AC 代码大力讨论每一种情况
输出方案直接枚举,计算需要的轮数判断一下就可以了
计算轮数的时候应该剪枝,才能保证单次是 (O(nm)) 的
//-std=c++11
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
struct IO{//-std=c++11,with cstdio and cctype
private:
static constexpr int ibufsiz = 1 << 20;
char ibuf[ibufsiz + 1],*inow = ibuf,*ied = ibuf;
static constexpr int obufsiz = 1 << 20;
char obuf[obufsiz + 1],*onow = obuf;
const char *oed = obuf + obufsiz;
public:
inline char getchar(){
#ifndef ONLINE_JUDGE
return ::getchar();
#else
if(inow == ied){
ied = ibuf + sizeof(char) * fread(ibuf,sizeof(char),ibufsiz,stdin);
*ied = ' ';
inow = ibuf;
}
return *inow++;
#endif
}
template<typename T>
inline void read(T &x){
static bool flg;flg = 0;
x = 0;char c = getchar();
while(!isdigit(c))flg = c == '-' ? 1 : flg,c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
if(flg)x = -x;
}
template <typename T,typename ...Y>
inline void read(T &x,Y&... X){read(x);read(X...);}
inline int readi(){static int res;read(res);return res;}
inline long long readll(){static long long res;read(res);return res;}
inline void flush(){
fwrite(obuf,sizeof(char),onow - obuf,stdout);
fflush(stdout);
onow = obuf;
}
inline void putchar(char c){
#ifndef ONLINE_JUDGE
::putchar(c);
#else
*onow++ = c;
if(onow == oed){
fwrite(obuf,sizeof(char),obufsiz,stdout);
onow = obuf;
}
#endif
}
template <typename T>
inline void write(T x,char split = ' '){
static unsigned char buf[64];
if(x < 0)putchar('-'),x = -x;
int p = 0;
do{
buf[++p] = x % 10;
x /= 10;
}while(x);
for(int i = p;i >= 1;i--)putchar(buf[i] + '0');
if(split != ' ')putchar(split);
}
inline void lf(){putchar('
');}
~IO(){
fwrite(obuf,sizeof(char),onow - obuf,stdout);
}
}io;
int n,m;
inline int f(const int a,const int b,const int c,int sum){
if(sum > n)return -0x7fffffff;
if(a == b)return 3;
else if(b == c)return 1;
else if(a == c)return 2;
else if(a >= b && b >= c)return f(b - c,b,c,sum + 2) + 2;
else if(a >= b && a <= c)return f(a,b,a - b,sum + 2) + 2;
else if(a >= c && a <= b)return f(a,a - c,c,sum + 1) + 1;
else if(b >= c && c >= a)return f(a,c - a,c,sum + 2) + 2;
else if(c >= b && b >= a)return f(a,b,b - a,sum + 1) + 1;
else if(a >= c && c >= b)return f(c - b,b,c,sum + 1) + 1;
}
// inline int f(const int a,const int b,const int c){
// if(a == b)return 3;
// if(a == c)return 2;
// if(b == c)return 1;
// const int mx = max({a,b,c});
// if(a == mx){
// const int t = f(abs(b - c),b,c),rem = t % 3;
// if(rem == 1)return t;
// if(rem == 2)return t + 2;
// if(rem == 0)return t + 1;
// }
// if(b == mx){
// const int t = f(a,abs(a - c),c),rem = t % 3;
// if(rem == 1)return t + 1;
// if(rem == 2)return t;
// if(rem == 0)return t + 2;
// }
// if(c == mx){
// const int t = f(a,b,abs(a - b)),rem = t % 3;
// if(rem == 1)return t + 2;
// if(rem == 2)return t + 1;
// if(rem == 0)return t;
// }
// }
struct node{int a,b,c;};
vector<node> ans;
int main(){
while(io.read(n,m),(n != -1 && m != -1)){
int pos,p1,p2;const int rem = n % 3;
if(rem == 0)pos = 3,p1 = 1,p2 = 2;
if(rem == 1)pos = 1,p1 = 2,p2 = 3;
if(rem == 2)pos = 2,p1 = 1,p2 = 3;
auto get = [pos,p1,p2](const int x,const int i){
if(x == pos)return m;
if(x == p1)return i;
if(x == p2)return m - i;
};
ans.clear();
for(int i = 1;i < m;i++)
if(f(get(1,i),get(2,i),get(3,i),0) == n)ans.push_back(node{get(1,i),get(2,i),get(3,i)});
io.write(ans.size(),'
');
for(auto x : ans)
io.write(x.a,' '),io.write(x.b,' '),io.write(x.c,'
');
}
return 0;
}