題目地址:http://codeforces.com/contest/602/problem/D
題意:給定n,q。n代表有n個點,橫座標是1到n。縱座標由輸入給定,之後有q(q<100)個問題求給定的區間[c,d]。求所有子串的斜率絕對值的最大值(子串中取兩點)之和。
題解:很容易發現,斜率絕對值的最大值一定是由相鄰的兩個點提供,每一個相鄰兩個點的斜率肯定有其管轄的範圍(少說是它本身)。則用優先隊列找到相鄰兩個點的斜率的向左管轄範圍和向右管轄範圍。則這個斜率提供的值為 abs(斜率)*(向左的範圍)*(向右的範圍)。詳見代碼。
#include <cstdio>#include <algorithm>#include <cstring>#define N 100005#define INF 1e9typedef long long ll;using namespace std;int a[N],b[N];pair<int,int>q[N],tmp;int vl[N],vr[N];int n,m,startn,endn;int main(){ int i; // freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<n;i++) //最大的abs(斜率)一定出現在相鄰的兩個點// b[i]=abs(a[i+1]-a[i]); b[0]=b[n]=INF; startn=endn=0; tmp.first=b[0];tmp.second=0; q[endn++]=tmp; for(i=1;i<n;i++) { while(startn<endn&&q[endn-1].first<=b[i])endn--; vl[i]=q[endn-1].second; tmp.first=b[i];tmp.second=i; q[endn++]=tmp; } startn=endn=0; tmp.first=b[n];tmp.second=n; q[endn++]=tmp; for(i=n-1;i>=1;i--) { while(startn<endn&&q[endn-1].first<b[i])endn--;//注意這裡是小於而不是小於等於,防止和之後的重複計算// vr[i]=q[endn-1].second; tmp.first=b[i];tmp.second=i; q[endn++]=tmp; } int c,d; while(m--) { scanf("%d%d",&c,&d); ll ans=0; for(i=c;i<d;i++) { ans+=(ll)((ll)(i-max(vl[i],c-1))*(min(vr[i],d+1)-i-1)*b[i]); } printf("%I64d\n",ans); } //查看這個斜率的管轄範圍。向左可以管轄多遠,向右可以管轄多遠// return 0;}