第一页结束之前的一道槛……
从中文题解到波兰文题解 , 折腾些时间才弄懂-_-# 。希望这篇题解能帮助后来者 , 不要浪费过多的时间捣鼓在google 翻译和晦涩难懂的文字之间……
提示:
1. 本题目前没有一个可行的贪心策略 , 每一次我们都需要考虑所有情况。
2. 在求i 的答案的时候我们先尝试去枚举每一个j , 你会发现 , 只会有那么几种情况。 而每一种情况把绝对值表达式拆开的最终形式都是一样的 , 这提示我们对于每一种情况最小值是可以维护的……
3. 你可能需要用一种数据结构去办到这一点。
详细题解在代码后:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 51000;
__inline int re() { int n; scanf("%d" , &n); return n; }
int n;
ll h[maxn] , res[maxn] , ori;
ll l[maxn] , r[maxn] , pl[maxn] , pr[maxn] , to[maxn];
void tension(ll& a , ll b) { a = min(a, b); }
ll cal(int x,int y)
{
if (x==y) return ori;
if (x>y) swap(x,y);
if (x==1&&y==n) return ori-abs(h[1]-h[2])-abs(h[n-1]-h[n])+abs(h[n]-h[2])+abs(h[1]-h[n-1]);
if (x==1&&y==2) return ori-abs(h[2]-h[3])+abs(h[1]-h[3]);
if (x==n-1&&y==n) return ori-abs(h[n-1]-h[n-2])+abs(h[n-2]-h[n]);
if (x+1==y) return ori-abs(h[x-1]-h[x])-abs(h[y+1]-h[y])+abs(h[x]-h[y+1])+abs(h[y]-h[x-1]);
long long Sum=ori;
if (x!=1) Sum=Sum-abs(h[x-1]-h[x])+abs(h[x-1]-h[y]);
Sum=Sum-abs(h[x+1]-h[x])+abs(h[x+1]-h[y]);
Sum=Sum-abs(h[y-1]-h[y])+abs(h[y-1]-h[x]);
if (y!=n) Sum=Sum-abs(h[y+1]-h[y])+abs(h[y+1]-h[x]);
return Sum;
}
bool cmp(int i , int j) { return h[i] < h[j]; }
int id[maxn] , id1[maxn] , id2[maxn];
vector<ll> dic;
void init()
{
for(int i=1;i<=n;i++) dic.push_back(h[i]) , id[i] = i;
sort(id+1, id+1+n, cmp);
sort(dic.begin(), dic.end());
for(int i=2;i<n;i++) l[i] = min(h[i-1] , h[i+1]) , r[i] = max(h[i-1], h[i+1]);
for(int i=1;i<=n;i++) to[id[i]] = i;
for(int i=2;i<n;i++) pl[i] = upper_bound(dic.begin(), dic.end(), l[i])-dic.begin();
for(int i=2;i<n;i++) pr[i] = upper_bound(dic.begin(), dic.end(), r[i])-dic.begin();
}
ll v[maxn*4][3];
int in[maxn];
ll A[3];
void update(int o , int l , int r , int k)
{
if(l==r) for(int i=0;i<3;i++) v[o][i] = A[i];
else
{
int mid = (l+r)/2;
if(to[k]<=mid) update(o*2, l, mid, k);
else update(o*2+1, mid+1, r, k);
for(int i=0;i<3;i++) v[o][i] = min(v[o*2][i] , v[o*2+1][i]);
}
}
ll query(int o , int l , int r , int L , int R , int k)
{
if(L<=l && r<=R) return v[o][k];
int mid = (l+r)/2;
ll res = INF;
if(L<=mid) res = min(res , query(o*2, l, mid, L, R, k));
if(R> mid) res = min(res , query(o*2+1, mid+1, r, L, R, k));
return res;
}
void clear()
{
for(int i=0;i<=n*4;i++) v[i][0] = v[i][1] = v[i][2] = INF;
for(int i=0;i<=n;i++) id[i] = id1[i] = id2[i] = i;
for(int i=0;i<=n;i++) in[i] = 0;
}
bool cmp1(int i , int j) { return h[i] > h[j]; }
bool cmp2(int i , int j) { return l[i] > l[j]; }
ll pointValue(int i){ return abs(h[i]-h[i-1])+abs(h[i]-h[i+1]); }
int sum[maxn][3];
void giveINF() { A[0] = A[1] = A[2] = INF; }
void giveOri(int k) { for(int i=0;i<3;i++) A[i] = sum[k][i]; }
void print(ll* a) { for(int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<endl; }
void print(int* a) { for(int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<endl; }
void perform1()
{
clear();
sort(id+1, id+n+1 , cmp1);
sort(id1+1, id1+n+1, cmp2);
int cnt = 1;
for(int i=1;i<=n;i++) if(id[i]!=1 && id[i]!=n)
{
while(cnt<=n && l[id1[cnt]] >= h[id[i]])
{
int k = id1[cnt];
if(k==1 || k==n) { cnt++; continue; }
ll vnow = h[k-1]+h[k+1]-pointValue(k);
sum[k][0] = A[0] = vnow - 2*h[k];
sum[k][1] = A[1] = vnow;
sum[k][2] = A[2] = vnow + 2*h[k];
update(1, 0, n, k);
in[k] = 1;
cnt++;
}
int k = id[i];
if(in[k-1]) giveINF() , update(1, 0, n, k-1);
if(in[k]) giveINF() , update(1, 0, n, k);
if(in[k+1]) giveINF() , update(1, 0, n, k+1);
ll vnow = ori-2*h[k] - pointValue(k);
tension(res[k], query(1, 0, n, 0, pl[k], 0)+vnow+l[k]+r[k]);
tension(res[k], query(1, 0, n, pl[k], pr[k], 1)+vnow+r[k]-l[k]);
tension(res[k], query(1, 0, n, pr[k], n, 2)+vnow-r[k]-l[k]);
if(in[k-1]) giveOri(k-1) , update(1, 0, n, k-1);
if(in[k]) giveOri(k) , update(1, 0, n, k);
if(in[k+1]) giveOri(k+1) , update(1, 0, n, k+1);
}
}
bool cmp3(int i , int j) { return h[i]<h[j]; }
bool cmp4(int i , int j) { return r[i]<r[j]; }
void perform2()
{
clear();
sort(id+1, id+n+1 , cmp3);
sort(id1+1, id1+n+1, cmp4);
int cnt = 1;
for(int i=1;i<=n;i++) if(id[i]!=1 && id[i]!=n)
{
while(cnt<=n && r[id1[cnt]] <= h[id[i]])
{
int k = id1[cnt];
if(k==1 || k==n) { cnt++; continue; }
ll vnow =-h[k-1]-h[k+1]-pointValue(k);
sum[k][0] = A[0] = vnow - 2*h[k];
sum[k][1] = A[1] = vnow;
sum[k][2] = A[2] = vnow + 2*h[k];
update(1, 0, n, k);
in[k] = 1;
cnt++;
}
int k = id[i];
if(in[k-1]) giveINF() , update(1, 0, n, k-1);
if(in[k]) giveINF() , update(1, 0, n, k);
if(in[k+1]) giveINF() , update(1, 0, n, k+1);
ll vnow = ori+2*h[k] - pointValue(k);
tension(res[k], query(1, 0, n, 0, pl[k], 0)+vnow+l[k]+r[k]);
tension(res[k], query(1, 0, n, pl[k], pr[k], 1)+vnow+r[k]-l[k]);
tension(res[k], query(1, 0, n, pr[k], n, 2)+vnow-r[k]-l[k]);
if(in[k-1]) giveOri(k-1) , update(1, 0, n, k-1);
if(in[k]) giveOri(k) , update(1, 0, n, k);
if(in[k+1]) giveOri(k+1) , update(1, 0, n, k+1);
}
}
bool cmp5(int i , int j) { return l[i]<l[j]; }
void perform3()
{
clear();
sort(id+1, id+n+1 , cmp3);
sort(id1+1, id1+n+1, cmp5);
sort(id2+1, id2+n+1, cmp4);
int cnt = 1 , cnt1 = 1;
for(int i=1;i<=n;i++) if(id[i]!=1 && id[i]!=n)
{
while(cnt<=n && l[id1[cnt]] <= h[id[i]])
{
int k = id1[cnt];
if(k==1 || k==n) { cnt++; continue; }
ll vnow = r[k]-l[k]-pointValue(k);
sum[k][0] = A[0] = vnow - 2*h[k];
sum[k][1] = A[1] = vnow;
sum[k][2] = A[2] = vnow + 2*h[k];
update(1, 0, n, k);
in[k] = 1;
cnt++;
}
while(cnt1<=n && r[id2[cnt1]] <= h[id[i]])
{
int k = id2[cnt1];
if(k==1 || k==n) { cnt1++; continue; }
giveINF();
update(1, 0, n, k);
in[k] = 0;
cnt1++;
}
int k = id[i];
if(in[k-1]) giveINF() , update(1, 0, n, k-1);
if(in[k]) giveINF() , update(1, 0, n, k);
if(in[k+1]) giveINF() , update(1, 0, n, k+1);
ll vnow = ori-pointValue(k);
tension(res[k], query(1, 0, n, 0, pl[k], 0)+vnow+l[k]+r[k]);
tension(res[k], query(1, 0, n, pl[k], pr[k], 1)+vnow+r[k]-l[k]);
tension(res[k], query(1, 0, n, pr[k], n, 2)+vnow-r[k]-l[k]);
if(in[k-1]) giveOri(k-1) , update(1, 0, n, k-1);
if(in[k]) giveOri(k) , update(1, 0, n, k);
if(in[k+1]) giveOri(k+1) , update(1, 0, n, k+1);
}
}
int main(int argc, char *argv[]) {
n = re();
for(int i=1;i<=n;i++) h[i] = re();
for(int i=2;i<=n;i++) ori += abs(h[i]-h[i-1]);
if(n<=2) { for(int i=1;i<=n;i++) printf("%lld\n" , ori); exit(0); }
for(int i=1;i<=n;i++) res[i] = ori;
for(int i=1;i<n;i++) tension(res[i], cal(i, i+1));
for(int i=2;i<=n;i++) tension(res[i], cal(i, i-1));
for(int i=2;i<=n;i++) tension(res[1], cal(1, i)) , tension(res[i], cal(1, i));
for(int i=1;i<n;i++) tension(res[n], cal(n, i)) , tension(res[i], cal(n , i));
init();
perform1();
perform2();
perform3();
for(int i=1;i<=n;i++) printf("%lld\n" , res[i]);
return 0;
}
我们可以分类讨论再用线段树维护……(如果看到这里不想看了 , 完全可以弃坑写 O(n2) 的暴力)
请按照我的提示2先枚举求一些答案 , 这样下面的思路会自然些:
现在我们正在求i 的答案 , 枚举到另一个数 j , 现在我们要交换i , j的值。 i , j原来在序列里和4个值有关(这个题头尾我们都进行特判 , 而且我们不考虑i , j相邻的情况 , 降低编程复杂度) , i , j交换后只有这4个值可能发生变化。
这4个值以前的值都是固定的 , 所以我们只考虑它们变化后的形式 , 我们先定义几个量。
hi→原来i位置上的值
Mini=min(hi−1,hi+1)
Maxi=max(hi−1,hi+1)
绝对值最后的展开形式与 , hi与Minj,Maxj关系 和 hj与Mini,Maxi关系 有关
可能的情况: hi<Minj;Minj≤hi<Maxj;Maxj≤hi
对于j 是类似的。
那么 , 根据乘法原理 , 总共有9种情况。
这9个情况的总体思路都是类似的 , 根据 hi与Minj,Maxj 的关系 , 逐渐把符合要求的加入一棵线段树中 , 再根据 hj与Mini,Maxi 的关系 , 在这棵线段树中查找符合条件的j。
下文中我们把 hi与Minj,Maxj 称作第一个关系 , hj与Mini,Maxi 称作第二个关系。
强调刚刚提到的几个地方:
由于加入条件由第一个关系控制 , 所以第一个关系相同的情况可以一起讨论 , 这样就有了代码中的perform1 , perform2 , perform3. 分别对应第一个关系的三种情况。
几个实现的细节:
最后再说明一下 , 这份代码中有很多特判的地方 , 我们在线段树中不讨论头尾和相邻的情况。