题意:给定长为n的数组a,a[i]属于[1, n]。开始时时刻为1,可以选定其中一个数,接下来每个时刻选定与已选定的数相邻的其他数,要求每个数被选定的时刻小于等于它的值。求满足这一条件的起点的数目。
解:对于每个a[i],其要求起点的范围为[i-a[i]+1, i+a[i]-1],将n个区间取交集,即为可能的答案区间。接下来考虑什么情况会没有解。举一个栗子:
5 6 4 1 4 5
左右两边都要求在时刻5及其之前被选定,但两者相距6个数,显然不可能满足条件。于是简单的得出一个结论:令numl[i]和numr[i]为数字i在a中最左边和最右边的位置,如果包括左右两点其距离大于i,那么无解。
(然后WA了)
事情并没有这么简单,再看一个栗子:
3 4 4 5 4
这种情况同样无解,虽然左边的4和右边的4之间刚好是4个数,但左边还有一个3,3肯定要在4之前被选定,于是就来不及选另一个4了。于是扩展结论:令numl[i]和numr[i]为数字i及小于i的数在a中最左边和最右边的位置,如果包括左右两点其距离大于i,那么无解。
在之前的基础上加一个类似前缀和的运算即可。
(有点丑陋的)代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define inf 0x3f3f3f3f int n; int a[maxx]={0}; int l[maxx]={0},r[maxx]={0}; int numl[maxx]={0},numr[maxx]={0}; signed main(){ int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } int s=1,e=n; for(int i=0;i<=n+1;i++){ numl[i]=n+1; numr[i]=0; l[i]=r[i]=0; } for(int i=1;i<=n;i++){ numl[a[i]]=i; numr[a[i]]=i; l[i]=max(s,i-a[i]+1); r[i]=min(e,i+a[i]-1); } for(int i=1;i<=n;i++){ numl[a[i]]=min(numl[a[i]],i); numr[a[i]]=max(numr[a[i]],i); } for(int i=1;i<=n;i++){ numl[i]=min(numl[i],numl[i-1]); } for(int i=1;i<=n;i++){ numr[i]=max(numr[i],numr[i-1]); } // printf("num i left pos:\n"); // for(int i=1;i<=n;i++) printf("%d ",numl[i]); // printf("\nnum i right pos:\n"); // for(int i=1;i<=n;i++) printf("%d ",numr[i]); printf("\n"); // for(int i=1;i<=n;i++) printf("%d ",l[i]); printf("\n"); // for(int i=1;i<=n;i++) printf("%d ",r[i]); printf("\n"); for(int i=1;i<=n;i++){ if(numr[i]-numl[i]+1>i){ printf("0\n"); goto nxt; } } for(int i=1;i<=n;i++){ s=max(s,l[i]); e=min(e,r[i]); } printf("%d\n",max(0,e-s+1)); nxt:; } return 0; }View Code
标签:maxx,选定,numr,int,numl,Speedbreaker,define From: https://www.cnblogs.com/capterlliar/p/18516651