P8960 折纸 题解
题目描述
题目需要你回答
题目想要知道的是,从左往右数第 Up;否则输出 Down。
题目思路
这道题有很多种解法。这里讲一讲我的思路。
我们可以手动折叠以下试一试,发现折痕的形成是很有规律的。每一次的折痕其实相当于在前一次的折痕之间进行一系列的“插入”操作。也即,第
图示:
而每一次插入的折痕也是有规律的,依次都是“峰,谷,峰,谷……”(这种情况编号为1)或“谷,峰,谷,峰……”(这种情况编号为2)交替出现。那么什么时候插入的是情况1、什么时候插入的是情况2呢?通过找规律发现,这其实和上一次折叠的方向有关。如果上一次是从左往右折叠,那么插入的将是情况1,否则将是情况2。那么,我们就可以根据这些规律来设计算法计算第
这里我们考虑用分治递推的方式来解决这个问题。假设我们现在已知第
思路明白了,代码就好些了。这里因为个人习惯加了一些看起来冗余的读入优化。代码很丑,请见谅。
代码
详见注释。
#include<cstdio>
#define f_inline inline __attribute__((always_inline))
using namespace std;
using UL=unsigned long;//用于存储非负整数
f_inline char gc()//快速读入一个字符
{
#define MAXN 100000
static char buf[MAXN],*l,*r;
return (l==r)&&(r=(l=buf)+fread(buf,1,MAXN,stdin),l==r)?EOF:*l++;
#undef MAXN
}
f_inline UL read()//快速读入一个非负整数
{
register char ch(gc());
register UL x(0);
while(ch<'0'||ch>'9')
ch=gc();
while(ch>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^48),
ch=gc();
return x;
}
f_inline void read_str(register char *p)//快速读入一个字符串
{
*p=gc();
while(*p<'0'||*p>'1')
*p=gc();
while(*p>='0'&&*p<='1')
*(++p)=gc();
*p='\0';
}
char s[65];//用于存储题目中描述的折叠方式
int main()
{
register UL t(read());//读入数据组数
while(t--)//重复进行t次
{
register const UL n(read()),k(read());//按照题目要求读入n和k的值
read_str(s+1);//读入字符串,下标从1开始
register UL cur(k),floor(n);//初始化cur和floor的值
while(!(cur&1))//只要没有达到边界条件就重复递推
cur>>=1,
--floor;
cur=(cur>>1)+1;
register const bool flag(floor == 1 || s[floor - 1] == '1');
//如果flag==true,那么当前第一次插入的是谷折,否则是峰折
//多次实验可以得到,如果上一次是向左折的那么flag<-true,否则flag<-false
fputs(flag?((cur&1)?"Down\n":"Up\n"):((cur&1)?"Up\n":"Down\n"),stdout);//按照题目要求输出
}
return 0;
}