https://codeforces.com/gym/102059/problem/L
题目大意:给定一个包含 n n n个元素的序列, q q q次询问,每次询问给一个值 d i s dis dis,然后把原序列分段:每次分段从当前下标开始(初始为 1 1 1),选择最长的且连续的单调非降序列或者单调下降序列,设其长度为 l e n len len,若 l e n > = d i s len>=dis len>=dis,则这一段的长度为 l e n len len,没有坏元素,否者这一段的长度为 d i s dis dis,坏元素的个数为 d i s − l e n dis-len dis−len,重复这一步骤直到分段完成。
思路: O ( n ) O(n) O(n)内可以计算出数组 f [ i ] 、 c [ i ] f[i]、c[i] f[i]、c[i], f [ i ] f[i] f[i]表示从 i i i开始的连续非降序列的长度, c [ i ] c[i] c[i]表示从 i i i开始的连续单调递减序列的长度,对于每一个询问,暴力计算即可。当然需要把计算过的答案记下来,这样复杂度最高也不过: n / 2 + n / 3 + n / 4 + … … + n / n = n l g n n/2+n/3+n/4+……+n/n=nlgn n/2+n/3+n/4+……+n/n=nlgn。注意边界条件。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int maxn=1e5+5;
struct node
{
int v1,v2;
node(int a=0,int b=0)
{
v1=a,v2=b;
}
}ans[maxn];
int a[maxn];
int c1[maxn],c2[maxn],c[maxn];
int n,q;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int pre=1;
a[0]=a[1],a[n+1]=a[n]-1;
for(int i=1;i<=n+1;i++)
{
if(a[i]>=a[i-1])
continue;
for(int j=pre;j<=i-1;j++)
c1[j]=i-1-j;
pre=i;
}
pre=n;
for(int i=n;i>=0;i--)
{
if(a[i]>a[i+1])
continue;
for(int j=pre;j>=i+1;j--)
c2[j]=pre-j;
pre=i;
}
for(int i=1;i<=n;i++)
c[i]=max(c1[i],c2[i]);
scanf("%d",&q);
int dis;
int tmp1,tmp2;
while(q--)
{
scanf("%d",&dis);
if(ans[dis].v1!=0)
printf("%d %d\n",ans[dis].v1,ans[dis].v2);
else
{
int i=1,times=0;
while(i<=n)
{
++ans[dis].v1;
tmp1=i+c[i]+1;
tmp2=i+dis;
tmp2=min(tmp2,n+1);
if(tmp1>=tmp2)
i=tmp1;
else
ans[dis].v2+=(tmp2-i-c[i]-1),i=tmp2;
}
printf("%d %d\n",ans[dis].v1,ans[dis].v2);
}
}
return 0;
}