聪明的学生(bzoj 2523)

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2523

聪明的学生

Description

一位教授逻辑学的教授有三名非常善于推理且精于心算的学生 A B C
有一天,教授给他们三人出了一道题:
教授在每个人脑门上贴了一张纸条并告诉他们,每个人的纸条上都写了一个正整数,且某两个数的和等于第三个。
于是,每个学生都能看见贴在另外两个同学头上的整数,但却看不见自己的数。

这时,教授先对学生 A 发问了:“你能猜出自己的数吗?” A 回答:“不能。”
教授又转身问学生 B :“你能猜出自己的数吗?” B 想了想,也回答:“不能。”
教授再问学生 C 同样的问题, C 思考了片刻后,摇了摇头:“不能。”
接着,教授又重新问 A 同样的问题,再问 B C ,…… 。
经过若干轮的提问之后,当教授再次询问某人时,此人突然露出了得意的笑容,把贴在自己头上的那个数准确无误的报了出来。

现在,如果告诉你:教授在第 N 次提问时,轮到回答问题的那个人猜出了贴在自己头上的数是 M ,你能推断出另外两个学生的头上贴的是什么数吗?

Input

输入文件包括若干组测试数据,其中的每一行代表一组测试数据,由两个整数 N M 组成(即在教授第 N 次提问时,轮到回答问题的那个人猜出了贴在自己头上的数是 M )。
两个数之间用空格分隔开。最后,由 “ 1 1 ” 组成的一行标志着输入数据的结束。

Output

按照输入文件中的顺序依次给出各组数据的结果。
文件中对应每组数据的输出的第一行是一个整数 p ,是可能情况的总数。
接下来的 p 行,每一行包括三个数,分别为贴在 ABC 头上的三个数。
输出时,所有解按照 A 头上的数增序排列;
A 头上的数相同的情况下,按照 B 头上的数增序排列。

Sample Input

5 8
3 2
2 3
-1  -1

Sample Output

3
2 8 6
5 8 3
6 8 2
1
1 1 2
1
2 3 1

HINT

在没有人猜出自己头上的数之前,大家对教授提问的回答始终都是“不能”;
而且除此之外在 A B C 之间是没有进行任何信息交流的。
也就是说,每个人推断的依据仅仅是另外两个人的头上数,以及大家对教授的提问所做出的否定回答。
教授总是从学生A开始提问的。
你可以假定,这三个足够聪明的学生能够根据已知的条件在最早的轮次猜出自己的数,并且永远都不会猜错。
稍经分析和推理,你将得出以下结论:总是头上贴着最大的那个数的人最先猜出自己头上的数。

对于 100% 的数据, 0<N<500 0<M<30000

Solution

根据提示,我们可以知道,最大的那个数必定为 m
假如我们知道,若三数分别为 a b c ,那么猜出头上的数需要教授最少提问多少次的话,问题就很容易解决:
只需要枚举其中一个数 x ,那么三个数分别就为 x mx m ,只需要判断这个最少提问次数(设为 fx,mx,m )是否等于 n 即可。

那么如何求出 fx,y,z 呢?
因为每次都是数字最大的人先猜出,所以我们只需要考虑这个人即可。

假设我是这个人。
我可以看到另外两个人的数字,分别为 x y
那么我就只可能为两种情况: |xy| x+y

x=y ,那么我就可以直接猜出自己为 x+y (因为 |xy| 0 ,而我是正整数)。
xy ,凭借上帝视角,我可以知道自己为 x+y ,所以我必须要排除掉自己为 |xy| 的情况。

用反证法。

如果我为 |xy| ,那么这三个数分别为 |xy| x y
那么除了我之外,另一个人将会在 f|xy|,x,y 次询问时猜出他自己的数字。

凭借上帝视角,我可以知道这是不可能的,那么我就可以排除掉这种可能性。
fx,y,z=f|xy|,x,y+ 那个在反证法中应该猜出自己数字的人到我的距离(注意,这里的距离指的是那个人被询问之后,还要询问多少次才能到我被询问,因为教授是依次询问的)。

至于猜出自己数字的人是谁,那就要看 x y 中哪个大一些了。

Code

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <algorithm>  
  4.   
  5. using namespace std;  
  6.   
  7. struct Three{  
  8.     int A,B,C;  
  9. };  
  10.   
  11. int n,z;  
  12. int tot;  
  13. Three q[30010];  
  14.   
  15. inline int in(){  
  16.     int ans=0;  
  17.     char x=getchar();  
  18.     while((x<‘0’||x>‘9’)&&x!=‘-‘)x=getchar();  
  19.     if(x==‘-‘)return -1;  
  20.     while(x>=‘0’&&x<=‘9’){ans=ans*10+x-‘0’;x=getchar();}  
  21.     return ans;   
  22. }  
  23.   
  24. inline int turn(int x,int y){  
  25.     return (y>x)?(y-x):(y+3-x);  
  26. }  
  27.   
  28. int ans(int x,int y,int s1,int s2,int s3){  
  29.     if(x==y)return s3;  
  30.     else{  
  31.         if(x>y)return ans(y,x-y,s2,s3,s1)+turn(s1,s3);  
  32.         return ans(x,y-x,s1,s3,s2)+turn(s2,s3);  
  33.     }  
  34. }  
  35.   
  36. inline void push(int x,int y,int z){  
  37.     q[++tot].A=x;q[tot].B=y;q[tot].C=z;  
  38. }  
  39.   
  40. bool cmp(Three a,Three b){  
  41.     return a.A<b.A||(a.A==b.A&&a.B<b.B);  
  42. }  
  43.   
  44. int main(){  
  45.     while(1){  
  46.         if((n=in())==-1)return 0;  
  47.         if((z=in())==-1)return 0;  
  48.         tot=0;  
  49.         int tmp=n%3;  
  50.         if(!tmp){  
  51.             for(int x=1;x<z;x++){  
  52.                 int y=z-x;  
  53.                 if(ans(x,y,1,2,3)==n)push(x,y,z);  
  54.             }  
  55.         }  
  56.         else if(tmp==1){  
  57.             for(int x=1;x<z;x++){  
  58.                 int y=z-x;  
  59.                 if(ans(x,y,2,3,1)==n)push(z,x,y);  
  60.             }  
  61.         }  
  62.         else if(tmp==2){  
  63.             for(int x=1;x<z;x++){  
  64.                 int y=z-x;  
  65.                 if(ans(x,y,1,3,2)==n)push(x,z,y);  
  66.             }  
  67.         }  
  68.         printf(”%d\n”,tot);  
  69.         for(int i=1;i<=tot;i++)printf(“%d %d %d\n”,q[i].A,q[i].B,q[i].C);  
  70.     }  
  71.     return 0;  
  72. }  

你可能感兴趣的:(分治)