样例1输入:
5 3
5 1 4
样例1输出:
9
样例2输入:
3 4
1 2 1 2
样例2输出:
3
样例1解析
拿第一个样例举例:
有5个单元格,要查询的单元格是5,1,4:
因为代币可以往相邻的格子里放,除了左右两端外,剩下的格子都可以往左右两边放(当然也可以不放):
那么一共就有13种放置的情况。
然后我们需要看那些情况一定可以发现代币。
首先可以确定的是有3种情况一定可以发现代币:
1.当查询单元格1时 代币在单元格1中 且 不往邻格移动的情况。
2.当查询单元格4时 代币在单元格4中 且 不往邻格移动的情况。
3.当查询单元格5时 代币在单元格5中 且 不往邻格移动的情况。
还有一种情况就是:
5.当代币在单元格5,向单元格4移动时的情况。
因为最后一次查询问的是单元格4,因此只要向单元格4移动代币,就一定可以发现代币。
13种情况减去这4种能发现代币的情况,总共有9种情况是不会发现代币的。
样例2解析
有3个单元格:1 2 3
有4次查询,分别查了两次单元格1,单元格2。
样例2告诉我们存在多次查询同一个单元格的情况。
也就是说如果当前单元格被查询过了我们把代表放到当前单元格并不一定是保险的,因为该单元格后面还是有可能会被查询。
我们预处理两个数组:first[],last[]。
分别表示当前单元格是否是第一次被遍历,是否是最后一次被遍历。
如果当前单元格(i)被查询过,且前一个单元格不是最后一次被查询,那么代表移动都有可能被发现。
如果当前单元格(i)第一次被查询,并且前一个单元格(i-1)已经是最后一次被查询,那代币就可以放心的移动到前一个单元格:
用公式表示为:
last[i-1]<first[i]
同理,后一个单元格如果已经是最后一次被查询,且当前单元格是第一次被查询,那代表就可以放心的从当前单元格移到后一个单元格。
用公式表示为:
last[i+1]<first[i]
还有一种情况就是当前单元格从头到尾都没有被查询过,那么代币放在当前单元格就是安全的,可以放在当前单元格子。
code
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],first[N],last[N];
int ans;
int main(){
cin>>n>>m;
memset(last,-1,sizeof last);
memset(first,INF ,sizeof first);
//m次查询
for(int i=1;i<=m;i++)
{
cin>>a[i];
first[a[i]]=min(first[a[i]],i);
last[a[i]]=max(last[a[i]],i);
}
for(int i=1;i<=n;i++)
{
if(i>1&&first[i]>last[i-1]) ans++; //代币向后一个单元格移动
if(i<n && first[i]>last[i+1]) ans++; //代币向前一个单元格移动
if(first[i]==INF) ans++; //当前单元格从头到尾没有被遍历过,代币可以放当前单元格
}
cout<<ans;
return 0;
}