6734. 【2020.06.18省選模擬】T2 航行

題目


正解

神仙題。
先考慮ai{1,2}a_i\in \{1,2\}的情況怎麼做。爲了方便記ai{0,1}a_i \in \{0,1\}
考慮一段00一段11這樣選,設狀態fi,0/1f_{i,0/1}表示搞完前ii個,ii選什麼,這時候的最小代價。
轉移爲fj,1+sum(j+1,i,0)fi,0f_{j,1}+sum(j+1,i,0)\to f_{i,0}(另一個同理)
其中sum(l,r,t)=i=l+1raitsum(l,r,t)=\sum_{i=l+1}^r|a_i-t|
顯然這個東西可以拆成前綴和。
考慮轉移對jj的限制。
Li,0/1,Ri,0/1L_{i,0/1},R_{i,0/1},表示ii0/10/1時,[Li,0/1,Ri,0/1][L_{i,0/1},R_{i,0/1}]內的數都要選0/10/1(也就是ii作爲最大值/最小值)。
(注意這個狀態跟題解中所說的是相反的)
於是顯然有:對於任意k[j+1,i]k\in[j+1,i][Lk,0,Rk,0][j+1,i][L_{k,0},R_{k,0}]\in [j+1,i]
拆開考慮,j<mink=j+1iLk,0j<\min_{k=j+1}^iL_{k,0}imaxk=j+1iRk,0i\geq \max_{k=j+1}^iR_{k,0}
發現這兩個都可以用單調棧維護,而且前者的分佈是離散的,後者的分佈是連續的。
我們要求兩個單調棧可以貢獻的部分的交集的貢獻。
將前者丟入線段樹中,然後對於後者,二分出可以貢獻的區間,將這個區間丟到線段樹上查。
時間複雜度是O(nlgn)O(n\lg n)

值域更大的時候怎麼做?
考慮整體二分,然後硬點每個數只能選midmidmid+1mid+1
做完之後,發現選midmid的數,它實際上選的數小於等於midmid
mid+1mid+1的數,它實際上選的數大於midmid
繼續二分下去即可。
O(nlgnlg1e5)O(n\lg n \lg 1e5)


代碼

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 100010
#define INF 10000000000ll
#define ll long long
int n,m;
int a[N],mx,b[N];
int p[N];
int oL[N][2],oR[N][2];
int L[N][2],R[N][2];
void getmin(int &a,int b){a=min(a,b);}
void getmax(int &a,int b){a=max(a,b);}
int abs(int a){return a>=0?a:-a;}
struct Segment_Tree{
	int n;
	ll t[N*4];
	int id[N*4];
	void Tbuild(int k,int l,int r){
		t[k]=INF,id[k]=-1;
		if (l==r) return;
		int mid=l+r>>1;
		Tbuild(k<<1,l,mid);
		Tbuild(k<<1|1,mid+1,r);
	}
	void Tchange(int k,int l,int r,int x,ll c){
		if (l==r){
			t[k]=c;
			id[k]=(c==INF?-1:x);
			return;
		}
		int mid=l+r>>1;
		if (x<=mid)
			Tchange(k<<1,l,mid,x,c);
		else
			Tchange(k<<1|1,mid+1,r,x,c);
		if (t[k<<1]<=t[k<<1|1])
			t[k]=t[k<<1],id[k]=id[k<<1];
		else
			t[k]=t[k<<1|1],id[k]=id[k<<1|1];
	}
	pair<ll,int> Tquery(int k,int l,int r,int st,int en){
		if (st<=l && r<=en)
			return {t[k],id[k]};
		int mid=l+r>>1;
		pair<ll,int> res={INF,-1};
		if (st<=mid)
			res=Tquery(k<<1,l,mid,st,en);
		if (mid<en)
			res=min(res,Tquery(k<<1|1,mid+1,r,st,en));
		return res;
	}
	void init(int _n){n=_n;Tbuild(1,0,n);}
	void change(int x,ll c){Tchange(1,0,n,x,c);}
	pair<ll,int> query(int l,int r){return l>r?make_pair(INF,-1):Tquery(1,0,n,l,r);}
} seg[2];
ll f[N][2];
int pre[N][2];
ll ps[N][2];
struct Info{int id,v;} sl[2][N],sr[2][N];
int tl[2],tr[2];
bool operator<(Info x,Info y){return x.v>y.v;}
int tmp[N];
void dfs(int i,int j,int *p,int &p0,int &p1){
	if (i==0)
		return;
	if (j==0){
		dfs(pre[i][j],j^1,p,p0,p1);
		for (int k=pre[i][j]+1;k<=i;++k)
			tmp[++p0]=p[k];
	}
	else{
		for (int k=i;k>pre[i][j];--k)
			tmp[--p1]=p[k];
		dfs(pre[i][j],j^1,p,p0,p1);
	}
}
void divide(int lv,int rv,int *p,int n){
	if (n==0)
		return;
	if (lv==rv){
		for (int i=1;i<=n;++i)
			b[p[i]]=lv;
		return;
	}
	int mid=lv+rv>>1;
	for (int i=1;i<=n;++i){
		L[i][0]=lower_bound(p+1,p+n+1,oL[p[i]][0])-p;
		R[i][0]=upper_bound(p+1,p+n+1,oR[p[i]][0])-p-1;
		L[i][1]=lower_bound(p+1,p+n+1,oL[p[i]][1])-p;
		R[i][1]=upper_bound(p+1,p+n+1,oR[p[i]][1])-p-1;
	}
	for (int i=1;i<=n;++i){
		ps[i][0]=ps[i-1][0]+abs(a[p[i]]-mid);
		ps[i][1]=ps[i-1][1]+abs(a[p[i]]-(mid+1));
	}
	f[0][0]=f[0][1]=0;
	tl[0]=tl[1]=tr[0]=tr[1]=1;
	sl[0][1]=sl[1][1]={0,1};
	sr[0][1]=sr[1][1]={0,0};
	seg[0].init(n),seg[0].change(0,f[0][1]/*-ps[0][0]*/);
	seg[1].init(n),seg[1].change(0,f[0][0]/*-ps[0][1]*/);
	for (int i=1;i<=n;++i){
		for (int j=0;j<2;++j){
			while (tl[j] && sl[j][tl[j]].v>L[i][j]){
				seg[j].change(sl[j][tl[j]].id,INF);
				--tl[j];
			}
			int lst=i;
			while (tr[j] && sr[j][tr[j]].v<=R[i][j]){
				lst=sr[j][tr[j]].id;
				--tr[j];
			}
			sr[j][++tr[j]]={lst,R[i][j]};
			auto pos=lower_bound(sr[j]+1,sr[j]+tr[j]+1,(Info){0,i});
			if (pos==sr[j]+tr[j]+1)
				f[i][j]=INF,pre[i][j]=-1;
			else{
				pair<ll,int> tmp=seg[j].query(pos->id,i-1);
				f[i][j]=tmp.first+ps[i][j];
				pre[i][j]=tmp.second;
			}
		}
		for (int j=0;j<2;++j){
			if (pre[i][j^1]!=-1){
				sl[j][++tl[j]]={i,i+1};
				seg[j].change(i,f[i][j^1]-ps[i][j]);
			}
			sr[j][++tr[j]]={i,0};
		}
	}
	int p0=0,p1=n+1;
	dfs(n,f[n][0]<f[n][1]?0:1,p,p0,p1);
	memcpy(p+1,tmp+1,sizeof(int)*n);
	divide(lv,mid,p,p0);
	divide(mid+1,rv,p+p0,n-p0);
}
int main(){
	freopen("sailing.in","r",stdin);
	freopen("sailing.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]),getmax(mx,a[i]);
	for (int i=1;i<=n;++i)
		oL[i][0]=oR[i][0]=oL[i][1]=oR[i][1]=i;
	for (int i=1;i<=m;++i){
		int op,l,r,k;
		scanf("%d%d%d%d",&op,&l,&r,&k);
		getmin(oL[k][op^1],l);
		getmax(oR[k][op^1],r);
	}
	for (int i=1;i<=n;++i)
		p[i]=i;
	divide(1,mx,p,n);
	ll ans=0;
	for (int i=1;i<=n;++i)
		ans+=abs(a[i]-b[i]);
	printf("%lld\n",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章