【ARC123D】Inc, Dec - Decomposition


傳送門

這場真的是 rp++ 場。B、C 題都是一眼搞了個亂搞做法結果是列表首A。D 題雖然沒搞出來但也是一眼猜到了貪心的結論,可惜沒往下面繼續分析。

由於 \(b,c\) 一個上升,一個下降,容易考慮這樣一種構造方案:

如果 \(a_i<a_{i+1}\)\(a\) 上升,那么我們就令 \(b_{i+1}=b_i+(a_{i+1}-a_i),c_{i+1}=c_i\). 就是在上升的 \(b\) 中加上這段值;反之,如果 \(a\) 下降,就在下降的 \(c\) 中體現這段值,即令 \(b_{i+1}=b_i,c_{i+1}=c_i+(a_{i}-a_{i+1}).\)

直覺來看,這是很顯然的。假設 \(i \rightarrow i+1\)\(a\) 中上升,那么我們不得不在 \(b\) 中至少加上 \((a_{i+1}-a_{i}).\) 如果 \(c\) 更小了,那么 \(b\) 增加的就要更多。由於 \(b\) 單調遞增 \(c\) 單調遞減,所以代價可能變少的情況(\(b\) 為負數且 \(c\) 為正數)的情況應該比較少。但是我們必須嚴謹地證明這個結論,否則就沒有意義了。

上面的構造方案,可以形式化地歸納為:對於每個 \(1 \le i \lt n\),要么 \(b_i=b_{i+1}\),要么 \(c_{i+1}\).

考慮反證法證明這一點,即存在一個位置 \(1\le k \le n\) ,使得 \(b_{k} < b_{k+1}\)\(c_{k} > c_{k+1}\) 同時存在。

容易發現我們可以對 \(b,c\)\(k\) 前綴一個 \(+1\) 一個 \(-1\) 或者對 \(b,c\)\(k+1\) 后綴一個 \(-1\) 一個 \(+1\) 來實現滿足單調性,\(b_i+c_i=a_i\) 的同時調整 \(b_i,b_{i+1}\) 還有 \(c_i,c_{i+1}\) 之間的大小關系。

但我們需要考慮正負以確定 \(b,c\) 每個元素的絕對值之和是否改變。容易發現,當 \(b_k<0\) 的時候,我們執行一次前綴操作,可以令 \(b_i\)\(b_{i+1}\) 更接近一步,同時絕對值之和不會更大(因為 \(b\) 的每個元素絕對值都少了 \(1\). 而 \(c\) 中每個對應元素的絕對值最多只會增加 \(1\).)。如果 \(b_k \ge 0\). 則 \(b_{k+1}>0\),那么我們執行一次后綴操作,同樣令 \(b_i\)\(b_{i+1}\) 更接近一步,類似地,絕對值之和也不會變大。所以不斷執行前綴 \(+1\) 和后綴 \(-1\) 操作就能令 \(b_i=b_{i+1}\). 同理,當然也能令 \(c_i=c_{i+1}\). 所以這種貪心構造的正確性得到了保證。(P.S. 如果 \(b_i\)\(b_{i+1}\) 的差大於 \(c_i\)\(c_{i+1}\) 的差,你會發現我們無法讓 \(b_i=b_{i+1}\). 因為調整的過程中 \(c_k\) 就會小於 \(c_{k+1}\). 但你發現這種時候我們就可以讓 \(c_k=c_{k+1}\). 同時不難發現,調整前 \(b_{i+1}-b_i\)\(c_i=c_{i+1}\) 的大小關系,就對應着 \(a_i\)\(a_{i+1}\) 的大小關系。這里各個方向聯系緊密,非常巧妙(AT就成天出這種思維題))。

題外話:如果覺得一加一減比較麻煩,最后證明的時候可以學官方題解把 \(c\) 取相反數化成 \(b-c=a\) ,但我覺得一般人在不知道結論證明前是不會想到這種方式的,AT的題解總是干這種事情讓我理解很困難。

到這里,我們就得到了貪心構造的方法。但是一個問題是我們還沒有確定 \(b_1,c_1\). 當確定了 \(b_1=x\) 后,我們就唯一確定了整個 \(b,c\).

一位六年級學弟賽場上用三分 \(x\) 加上這個貪心構造的方法 AC 了 orz orz orz. 但我們實際上不需要三分,有更嚴謹的 \(O(n)\) 做法。

首先,我們將 \(b_{i}\) 轉移到 \(b_{i+1}\) 所加的量記作 \(\Delta_i\). 類似地,把 \(c_i\) 轉移到 \(c_{i+1}\) 所減的量記作 \(\Gamma_i\). 容易發現 \(\Delta\)\(\Gamma\) 都是非負的。容易想到寫出 \(b_1=x\) 后整個序列的樣子:

\[b=x,x+\Delta_1,x+\Delta_1+\Delta_2,... \]

類似地:

\[c=a_1-x,a_1-x+\Gamma _1,a_1-x+\Gamma_1+\Gamma_2,... \]

那么你發現 \(b,c\) 其實有一部分都是固定的,只不過 \(b\) 的每個元素整體加上 \(x\)\(c\) 的每個元素整體減去 \(x\). 現在的問題是,給定若干個關於 \(x\) 的一次式子,求其最小的絕對值和。這是初一數學里每個人都會學到的知識,也是一個玩爛的 trick:把每個式子寫成 \(|x-a|\) 的形式,然后 \(x\) 就是所有 \(a\) 的中位數。

上面的東西顯然都可以寫成 \(x-a\) 的形式,對於 \(b\) 來說,其可以寫成:

\[x-(-\Delta_1-\Delta_2-...) \]

的形式。對於 \(c\) 來說,我們無法進行恆等變形,但考慮相反數的絕對值相等,可以取相反數改寫成:

\[x-(a_1-\Gamma_1-\Gamma_2-...) \]

的形式。

那么 \(\Delta\)\(\Gamma\) 容易在 \(O(n)\) 的時間內求出,自然改寫后每個 \(|x-a|\)\(a\) 也能在 \(O(n)\) 的時間內算出。最后求 \(2n\) 個元素的中位數,顯然也是可以 \(O(n)\) 內分治(或者 nth_element)實現的。當然,直接 sort 也沒有任何問題,誰不想偷懶呢。

不用感受函數的凹凸性(霧)。我們就在 \(O(n \log n) / O(n)\)(吊打斜率優化)的復雜度內解決了這個 D.

#include<bits/stdc++.h>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define per(i,a,b) for(ll i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
using namespace std;
const int MAXN=4e5+10;
ll n,a[MAXN],db[MAXN],dc[MAXN],b[MAXN],c[MAXN],d[MAXN],ans;
int main(){
	cin>>n;
	rep(i,1,n){
		cin>>a[i];
	}
	rep(i,1,n-1){
		if(a[i]<a[i+1]){
			db[i]=a[i+1]-a[i]; 
		}else{
			dc[i]=a[i]-a[i+1];
		}
	}
	ll sum=0;
	rep(i,1,n){
		d[i]=sum;
		sum-=db[i];		
	}
	sum=a[1];
	rep(i,1,n){
		d[i+n]=sum;
		sum-=dc[i];
	}
	sort(d+1,d+1+2*n);
	b[1]=d[n];
	c[1]=b[1]-a[1];
	rep(i,1,n){
		b[i+1]=b[i]+db[i];
		c[i+1]=c[i]+dc[i];
	}
	rep(i,1,n){
		ans+=abs(b[i]);
		ans+=abs(c[i]);
	}
	cout<<ans<<endl;
	return 0;
}

DP 解法有時間再補(之前寫了那么多斜率優化都沒想到 DP 解法確實需要好好學習)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM