萌萌哒十五酱的情书~ | ||||||
| ||||||
Description | ||||||
因为十五酱实在是太萌了所以她经常会收到爱慕者的情书!!!!!!!!!!~ 当然因为情书太多了所以她没有时间都读完 于是她把给她的情书从左到右划分成n个区域,在每个区域的边界依次表上0~n,然后她将经行m次操作,每次她都会选择一个数字ai,把纸带沿这个数字当前所在的位置翻折(假如已经在边界上了那就相当于什么都不做啦~)。 十五想知道经过m次对折之后她还需要读多长的情书。 | ||||||
Input | ||||||
多组数据 第一行为两个正整数n(1<=n<=10^18),m(m<=3000),表示纸带的长度和操作的次数。 接下来的一行为m个整数ai(1<=ai<=n),其中ai表示第i次选择的数字。 | ||||||
Output | ||||||
多组数据输出 输出文件中每组数据输出一行为一个整数,即纸带最后的长度。 | ||||||
Sample Input | ||||||
5 2 3 5 | ||||||
Sample Output | ||||||
2 | ||||||
Hint | ||||||
十五酱最萌了昂~ | ||||||
Source | ||||||
HCPC2014校赛训练赛 6 |
思路(很不错的一道题):
1、我们最后要得到最终区间的长度,那么我们考虑动态维护区间的左右端点值l,r。
初始设定l=0,r=n;
2、如果对应当前的折叠位子在(l,r)中,那么很显然,我们直接折叠即可,如果折叠点靠右,那么r=折叠点,相反,l=折叠点。做到动态维护l,r的过程。
相反,如果对应当前的折叠位子不在(l,r)中,我们考虑找到这个取代位子pos,使得pos在(l,r)中。
假设(样例中折叠第一次之后的样子):
此时我们要在位子5进行折叠,显然此时l=0,r=3,5不在(l,r)中,那么我们考虑如何找到一个替代位子pos(本情况其实就是1),使得pos在(l,r)中,继续进行动态维护。
我们考虑,原先我们这个3的维护是通过判断折叠位子(3)是靠右边近一些还是靠左边近一些得到的,那么我们这个5,显然是从3的右边折过来的,那么这个替代位子pos=2*r-5
那么如果我们是已经折叠了多次,那么我们就对其进行多次操作,找寻这个替代点即可。
那么我们需要记录每一次折叠之后的区间左右端点,我们对其每一次进行记录,再对于每一次操作之后的替代点进行找寻即可。
(这里说的可能有点乱,大家可以更好的去参考代码)
3、每一次我们都找寻到一个替代点之后,然后在动态维护区间左右端点位子即可。
Ac代码:
#include<stdio.h>
#include<string.h>
using namespace std;
#define ll long long int
struct node
{
ll l,r;
}tmp[3002];
ll pos[3002];
int main()
{
ll n;
int m;
while(~scanf("%lld%d",&n,&m))
{
ll l=0,r=n;
for(int i=0;i<m;i++)
{
scanf("%lld",&pos[i]);
}
for(int i=0;i<m;i++)
{
if(pos[i]>l&&pos[i]<r)
{
if(pos[i]>(l+r)/2)r=pos[i];
else l=pos[i];
}
else
{
for(int j=0;j<i;j++)
{
if(pos[i]<tmp[j].l)pos[i]=tmp[j].l*2-pos[i];
else if(pos[i]>tmp[j].r)pos[i]=tmp[j].r*2-pos[i];
}
if(pos[i]>l&&pos[i]<r)
{
if(pos[i]>(l+r)/2)r=pos[i];
else l=pos[i];
}
}
tmp[i].l=l;tmp[i].r=r;
}
printf("%lld\n",r-l);
}
}