题解 God Knows

传送门

yysy,我考场上连\(n^2\)的暴力都没搞出来
这里实际上求的是最小权极大上升子序列
但这个跟题目几乎没什么直接联系,貌似只是因为极大上升子序列一定是符合题意的一组解
然后题里要求总权值最小,所以是最小权极大上升子序列
\(n^2\)代码:

int minn, mindp;
for (int i=1; i<=n; ++i) {
	minn=INF, mindp=INF;
	for (int j=i-1; j; --j) if (p[j]<p[i]) {if (p[i]-p[j]<minn) minn=p[i]-p[j], mindp=min(mindp, dp[j]);}
	dp[i]+=mindp==INF?0:mindp;
	for (int j=i-1; j; --j) if (p[j]<p[i]) vis[j]=1;
	dp[i]+=c[i];
}
for (int i=1; i<=n; ++i) if (!vis[i]) ans=min(ans, dp[i]);
printf("%d\n", ans);

然后正解:
考虑如何求出所有极大上升子序列中的最小总权值
首先发现对于一个位置i,它左侧所有可能作为可以转移的子序列有端点的点j可以用一个单调栈维护
但由于有个\(p[i]\)的限制,这个单调栈貌似在一个位置弹过元素后就失效了
其实这里可以用线段树去维护它
这个\(p[i]\)的限制可以用一个「翻转坐标系」的技巧处理掉

  • 当出现形如「\(p[i]<p[j]\)的前提下,对符合要求的\(i\)\(j\)进行操作」的限制条件时,可以通过将\(p[\ ]\)翻转为\(x\)轴,\(i\)翻转为\(y\)轴的方式
    通过翻转坐标系将这样的限制条件转化为一个序列上的区间操作

  • 「线段树维护单调栈」:给出一个序列,这个序列的每个位置有两个值 \(a_i,f_i\),每次询问一个区间,把这个区间的所有数以\(a\)为关键字,从左到右做一个单调递减的栈,求这个单调栈中的元素的\(f\)值的最小值。

具体实现为对于每个区间,额外维护\(rmx[\ ],f_{min}[\ ],val[\ ]\)三个数组
\(rmx[\ ]\)记录当前区间的右子区间中的最大值,实际上就是左子区间中小于\(rmx[p]\)的数应该被弹掉
\(f_{min}[\ ]\)记录query(p<<1, l, mid, rmx[p]),这个东西在pushup时就可以维护出来,其意义在于确保query是\(O(nlog^2n)\)
\(val[\ ]\)就是记录\(f_i\)用的,但在这里非常容易搞混
核心在于这个写的有点麻烦的query函数

int query(int p, int l, int r, int q) {
	if (l<=tl(p)&&r>=tr(p)) {
		if (!maxn(p)) return INF;
		if (tl(p)==tr(p)) return maxn(p)>q?dp(maxn(p)):INF;
		if (q>rmx(p)) return query(p<<1, l, r, q);
		else return min(fmn(p), query(p<<1|1, l, r, q));
	}
	int mid=(tl(p)+tr(p))>>1, ans=INF;
	if (r>mid) {
		int rmax=qmax(p<<1|1, mid+1, r);
		if (q>rmax) {
			if (l<=mid) return query(p<<1, l, r, q);
			else return INF;
		}
		else {
			if (l<=mid) return min(query(p<<1, l, r, rmax), query(p<<1|1, l, r, q));
			else return query(p<<1|1, l, r, q);
		}
	}
	else return query(p<<1, l, r, q);
}

总时间复杂度\(O(nlog^2n)\)

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int p[N], c[N];

namespace force{
	int head[N], size, ans=INF;
	bool vis[N];
	struct edge{int to, next;}e[N*100];
	inline void add(int s, int t) {edge* k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;}
	void dfs(int sum) {
		//cout<<"dfs "<<sum<<endl;
		bool cge[30], all=1;
		memset(cge, 0, sizeof(bool)*(n+5));
		for (int i=1; i<=n; ++i) {
			if (!vis[i]) {
				vis[i]=1;
				for (int j=head[i],v; j; j=e[j].next) {
					v = e[j].to;
					if (!vis[v]) {
						cge[v]=1;
						vis[v]=1;
					}
				}
				dfs(sum+c[i]);
				vis[i]=0;
				for (int j=head[i],v; j; j=e[j].next) {
					v = e[j].to;
					if (cge[v]) vis[v]=0;
				}
				all=0;
			}
		}
		if (all) ans=min(ans, sum);
	}
	void solve() {
		for (int i=1; i<=n; ++i) {
			for (int j=i-1; j; --j) if (p[j]>p[i]) add(i, j); //, cout<<"add "<<i<<' '<<j<<endl;
			for (int j=i+1; j<=n; ++j) if (p[j]<p[i]) add(i, j); //, cout<<"add "<<i<<' '<<j<<endl;
		}
		dfs(0);
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task1{
	int r[N];
	int head[N], size, ans=INF;
	bool vis[N];
	struct edge{int to, next;}e[N*100];
	inline void add(int s, int t) {edge* k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;}
	void solve() {
		for (int i=1; i<=n; ++i) r[i]=i;
		for (int i=1; i<=n; ++i) {
			for (int j=i-1; j; --j) if (p[j]>p[i]) add(i, j); //, cout<<"add "<<i<<' '<<j<<endl;
			for (int j=i+1; j<=n; ++j) if (p[j]<p[i]) add(i, j); //, cout<<"add "<<i<<' '<<j<<endl;
		}
		int sum, cnt, cnt2=0;
		while (++cnt2%5 || clock()<=600000) {
			sum=0; cnt=0;
			random_shuffle(r+1, r+n+1);
			memset(vis, 0, sizeof(bool)*(n+5));
			for (int i=1; i<=n; ++i) {
				if (vis[r[i]]) continue;
				sum+=c[r[i]]; ++cnt; vis[r[i]]=1;
				for (int j=head[r[i]],v; j; j=e[j].next) {
					v = e[j].to;
					if (!vis[j]) {vis[j]=1; ++cnt;}
				}
				if (sum>ans) goto jump;
				if (cnt==n) {ans=min(ans, sum); goto jump;}
			}
			jump: ;
		}
		printf("%d\n", ans);
		//cout<<cnt2<<endl;
		exit(0);
	}
}

namespace task2{
	int dp[N], ans=INF;
	bool vis[N];
	void solve() {
		int minn, mini=0, mindp;
		for (int i=1; i<=n; ++i) {
			cout<<"i: "<<i<<" p[i]: "<<p[i]<<endl;
			minn=INF, mindp=INF;
			for (int j=i-1; j; --j) if (p[j]<p[i]) {/*cout<<"1: "<<p[j]<<endl;*/ if (p[i]-p[j]<minn) minn=p[i]-p[j], mini=j, mindp=min(mindp, dp[j]), cout<<"2: "<<p[j]<<endl;}
			dp[i]+=mindp==INF?0:mindp;
			for (int j=i-1; j; --j) if (p[j]<p[i]) vis[j]=1;
			dp[i]+=c[i];
		}
		//for (int i=1; i<=n; ++i) cout<<vis[i]<<' '; cout<<endl;
		//for (int i=1; i<=n; ++i) cout<<dp[i]<<' '; cout<<endl;
		for (int i=1; i<=n; ++i) if (!vis[i]) ans=min(ans, dp[i]);
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	const int SIZE=N<<2;
	int tl[SIZE], tr[SIZE], maxn[SIZE], rmx[SIZE], fmn[SIZE], dp[SIZE];
	#define tl(p) tl[p]
	#define tr(p) tr[p]
	#define maxn(p) maxn[p]
	#define dp(p) dp[p]
	#define rmx(p) rmx[p]
	#define fmn(p) fmn[p]
	int qmax(int p, int l, int r) {
		if (l<=tl(p)&&r>=tr(p)) return maxn(p);
		int mid=(tl(p)+tr(p))>>1, ans=0;
		if (l<=mid) ans=max(ans, qmax(p<<1, l, r));
		if (r>mid) ans=max(ans, qmax(p<<1|1, l, r));
		return ans;
	}
	int query(int p, int l, int r, int q) {
		//cout<<"query "<<p<<' '<<l<<' '<<r<<' '<<q<<endl;
		if (l<=tl(p)&&r>=tr(p)) {
			if (!maxn(p)) return INF;
			//cout<<"pos1"<<endl;
			if (tl(p)==tr(p)) return maxn(p)>q?dp(maxn(p)):INF;
			//cout<<"pos2"<<endl;
			if (q>rmx(p)) return query(p<<1, l, r, q);
			else return min(fmn(p), query(p<<1|1, l, r, q));
		}
		int mid=(tl(p)+tr(p))>>1, ans=INF;
		if (r>mid) {
			int rmax=qmax(p<<1|1, mid+1, r);
			if (q>rmax) {
				if (l<=mid) return query(p<<1, l, r, q);
				else return INF;
			}
			else {
				if (l<=mid) return min(query(p<<1, l, r, rmax), query(p<<1|1, l, r, q));
				else return query(p<<1|1, l, r, q);
			}
		}
		else return query(p<<1, l, r, q);
	}
	void pushup(int p) {
		maxn(p)=max(maxn(p<<1), maxn(p<<1|1));
		rmx(p)=maxn(p<<1|1);
		fmn(p)=query(p<<1, tl(p<<1), tr(p<<1), rmx(p));
		//cout<<p<<": "<<maxn(p)<<' '<<rmx(p)<<' '<<fmn(p)<<endl;
	}
	void build(int p, int l, int r) {
		tl(p)=l; tr(p)=r; fmn(p)=INF;
		if (l>=r) return ;
		int mid=(l+r)>>1;
		build(p<<1, l, mid);
		build(p<<1|1, mid+1, r);
	}
	void upd(int p, int pos, int val) {
		//cout<<"upd "<<p<<' '<<tl(p)<<' '<<tr(p)<<' '<<pos<<' '<<val<<endl;
		if (tl(p)==tr(p)) {maxn(p)=rmx(p)=fmn(p)=val; return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (pos<=mid) upd(p<<1, pos, val);
		else upd(p<<1|1, pos, val);
		pushup(p);
	}
	void solve() {
		build(1, 1, n);
		for (int i=1,t; i<=n; ++i) {
			//cout<<i<<": "<<query(1, 1, p[i], 0)<<endl;
			t=query(1, 1, p[i], 0);
			//cout<<endl;
			dp[i]=(t==INF?0:t)+c[i];
			upd(1, p[i], i);
		}
		printf("%d\n", query(1, 1, n, 0));
		exit(0);
	}	
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read();
	for (int i=1; i<=n; ++i) p[i]=read();
	for (int i=1; i<=n; ++i) c[i]=read();
	//if (n<=15) force::solve();
	//else task1::solve();
	//task2::solve();
	task::solve();
	
	return 0;
}
posted @ 2021-07-16 16:33  Administrator-09  阅读(43)  评论(0编辑  收藏  举报