题目:http://codeforces.com/contest/602/problem/D
题意:给定n(n<=1e5)个整数的数组h[i],现在定义一个函数
For an array , we define it's Lipschitz constant
as follows:
- if n < 2,
- if n ≥ 2,
over all 1 ≤ i < j ≤ n
分析:
Orz。不会做,借助@浩哥的神力做出来了。
首先分析L(h)函数,从定义上看就是斜率的绝对值的最大值。
对于(i,h[i])和(j,h[j]) ,j-i>=2,肯定存在一点(k,h[k]),i<k<j,使得abs(h[k]-h[i]/k-i) 或者 abs(h[k]-h[j]/k-j) 大于 abs(h[i]-h[j]/i-j)。可以在纸上画一下。
这就说明,函数的最大值一定是相邻两点之间的斜率的绝对值。
即长度为n的区间,只需考虑n-1种情况即可。
现在将abs(h[i]-h[i+1])存到另外一个数组use[i]。
对于每次查询[l,r],要求出所有子区间的解,只需考虑use[i]所影响的范围。
那么就是在i的左边找一个离i最近的且比use[i]大的数的位置p1。(注意p1要在[l,r]内)
在i的右边找一个离i最近的且大于等于use[i]的数的位置p2。
use[i]影响的区间的个数就是(i-p1+1)*(p2-i+1)。
那么use[i]对答案的贡献就是use[i]*(i-p1+1)*(p2-i+1)。
现在的问题就是怎样求在i的左边找一个离i最近的且比use[i]大的数的位置p1。
①线段树或者RMQ维护区间最大值。
然后二分打表。(不打表超时。。。。。)
②也可以用单调栈O(n)打表。
③相关题目:http://blog.youkuaiyun.com/w20810/article/details/46120455
本题应该可以用类似迭代的解法。
线段树+二分
代码:
#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e6+7;
int h[maxn],use[maxn];
int n;
inline int ABS(int a)
{
return a<0?-a:a;
}
int tree[maxn<<2];
void update(int pos,int v,int l,int r,int rt)
{
if(l==r)
{
tree[rt]=v;
return ;
}
int m=(l+r)>>1;
if(pos<=m)
update(pos,v,lson);
else
update(pos,v,rson);
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l && r<=R)
return tree[rt];
int m=(l+r)>>1,ret=-INF;
if(L<=m)
ret=max(ret,query(L,R,lson));
if(R>m)
ret=max(ret,query(L,R,rson));
return ret;
}
int FindL(int pos)
{
int down=1,up=pos-1,mid,ret=-1;
while(down<=up)
{
mid=(down+up)>>1;
if(query(mid,pos-1,1,n-1,1)>use[pos])
{
down=mid+1;
if(ret<mid)
ret=mid;
}
else
up=mid-1;
}
return ret;
}
int FindR(int pos)
{
int down=pos+1,up=n-1,mid,ret=n;
while(down<=up)
{
mid=(down+up)>>1;
if(query(pos+1,mid,1,n-1,1)>=use[pos])
{
up=mid-1;
if(ret>mid)
ret=mid;
}
else
down=mid+1;
}
return ret;
}
int LLL[maxn],RRR[maxn];
int main()
{
int q,i,j;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
for(i=1;i<n;i++)
{
use[i]=ABS(h[i]-h[i+1]);
update(i,use[i],1,n-1,1);
}
for(i=1;i<n;i++)
{
LLL[i]=FindL(i);
RRR[i]=FindR(i);
}
int L,R;
while(q--)
{
scanf("%d%d",&L,&R);
R--;
LL ans=0;
for(i=L;i<=R;i++)
{
int l=max(L-1,LLL[i])+1; l=min(l,i);
int r=min(R+1,RRR[i])-1; r=max(r,i);
ans+=use[i]*(r-i+1ll)*(i-l+1LL);
}
printf("%lld\n",ans);
}
return 0;
}
单调栈
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e5+8;
int L[maxn],R[maxn];
int h[maxn];
int Stack[maxn],pos[maxn];
int use[maxn];
int main()
{
int n,q,i,j,k;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
int top=0;
Stack[0]=INF;
pos[0]=0;
for(i=1;i<n;i++)
{
int v=abs(h[i]-h[i+1]);
use[i]=v;
while(top>0 && Stack[top]<v)
top--;
L[i]=i-pos[top];
Stack[top+1]=v;
pos[top+1]=i;
top++;
}
top=0;
Stack[0]=INF;
pos[0]=n;
for(i=n-1;i>=1;i--)
{
int v=abs(h[i]-h[i+1]);
while(top>0 && Stack[top]<=v)
top--;
R[i]=pos[top]-i;
Stack[top+1]=v;
pos[top+1]=i;
top++;
}
int l,r;
while(q--)
{
scanf("%d%d",&l,&r);
r--;
LL ans=0;
for(i=l;i<=r;i++)
{
int lq=max(l-1,i-L[i])+1;
int rq=min(r+1,i+R[i])-1;
ans+=use[i]*(rq-i+1ll)*(i-lq+1LL);
}
printf("%lld\n",ans);
}
return 0;
}
cf上的单调栈
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e5+7;
int h[maxn];
struct Stack
{
LL sum;
int v,pos;
}st[maxn];
int main()
{
int n,q,i,j,L,R;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
while(q--)
{
scanf("%d%d",&L,&R);
int top=0;
st[0].pos=L;
st[0].sum=0;
st[0].v=0;
LL ans=0;
for(i=L+1;i<=R;i++)
{
int v=abs(h[i]-h[i-1]);
while(top>0 && st[top].v<v)
top--;
LL s=st[top].sum+v*1ll*(i-st[top].pos);
ans+=s;
st[top+1].sum=s;
st[top+1].v=v;
st[top+1].pos=i;
top++;
}
printf("%lld\n",ans);
}
return 0;
}
迭代
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e5+7;
int h[maxn],L[maxn],R[maxn],s[maxn];
int main()
{
int n,q,i,j,x,l,r;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
for(i=0;i<=n;i++)
L[i]=R[i]=i;
for(i=1;i<n;i++)
s[i]=abs(h[i]-h[i+1]);
s[0]=INF;
for(i=1;i<n;i++)
while(s[i]>s[L[i]-1])
L[i]=L[L[i]-1];
s[n]=INF;
for(i=n-1;i>=1;i--)
while(s[i]>=s[R[i]+1])
R[i]=R[R[i]+1];
while(q--)
{
scanf("%d%d",&l,&r);
r--;
LL ans=0;
for(i=l;i<=r;i++)
{
int lq=max(l,L[i]);
int rq=min(r,R[i]);
ans+=s[i]*(rq-i+1ll)*(i-lq+1LL);
}
printf("%lld\n",ans);
}
return 0;
}