题解 序列问题

传送门

\(n^2\) 很好写
发现转移要满足限制条件 \(i<j,\ a[i]-i\geqslant a[j]-j,\ a[i]<a[j]\),于是CDQ,被卡常了
第一次写CDQ优化DP是在考场上写的居然还写出来了
然而被出题人耍了……
观察这几个条件,发现若满足 \(a[i]-i\geqslant a[j]-j,\ a[i]<a[j]\) 则一定有 \(i<j\) 成立
所以按 \(a[i]\) 排个序后就是找前面 \(a[i]-i\) 大的转移了
然后每个决策点的贡献只有1
所以其实直接求个最长不下降子序列就行了

  • 所以以后推出来限制条件或者不等式啥的先看看能不能合并啥的
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500050
#define ll long long
//#define int long long
#define max(a, b) ((a)>(b)?(a):(b))

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
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 a[N];

namespace force{
	int dp[1010][1010], ans;
	void solve() {
		for (int i=1; i<=n; ++i) {
			for (int j=1; j<=i; ++j) {
				dp[i][j]=max(dp[i-1][j], dp[i-1][j-1]+(a[i]==j?1:0));
				ans=max(ans, dp[i][j]);
			}
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task1{
	int dp[N], ans;
	void solve() {
		memset(dp, -1, sizeof(dp));
		dp[0]=0;
		for (int i=1; i<=n; ++i) {
			for (int j=0; j<i; ++j) if (a[i]>a[j] && a[i]-i<=a[j]-j) 
				dp[i]=max(dp[i], dp[j]);
			if (~dp[i]) ++dp[i];
			ans=max(ans, dp[i]);
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task2{
	int dp[N], ans, uni[N], usize, bin[N], lim, tot;
	struct grp{int a, b, c; inline void build(int x, int y, int z) {a=x; b=y; c=z;}}p[N];
	inline bool cmp1(grp a, grp b) {return a.a==b.a?(a.b==b.b?a.c<b.c:a.b>b.b):a.a<b.a;}
	inline bool cmp2(grp a, grp b) {return a.b==b.b?a.c<b.c:a.b>b.b;}
	inline void upd(int i, int dat) {++i; for (; i<=lim; i+=i&-i) bin[i]=max(bin[i], dat);}
	inline int query(int i) {++i; int ans=-1; for (; i; i-=i&-i) ans=max(ans, bin[i]); return ans;}
	inline void del(int i) {++i; for (; i<=lim; i+=i&-i) bin[i]=-1;}
	void cdq(int l, int r) {
		if (l==r) {
			if (p[l].a!=0 && ~dp[p[l].a]) ++dp[p[l].a];
			return ;
		}
		int mid=(l+r)>>1;
		cdq(l, mid);
		sort(p+l, p+mid+1, cmp2); sort(p+mid+1, p+r+1, cmp2);
		// cout<<"lr: "<<l<<' '<<r<<endl;
		// cout<<"p: "; for (int i=l; i<=r; ++i) cout<<p[i].a<<' '; cout<<endl;
		int pos1=l, pos2=mid+1;
		for (; pos2<=r; ++pos2) {
			while (pos1<=mid && p[pos1].b>=p[pos2].b) upd(p[pos1].c, dp[p[pos1].a]), ++pos1;
			// if (l==3 && r==4) cout<<"pos: "<<pos1<<' '<<pos2<<endl;
			// cout<<"c: "<<p[pos1-1].c<<' '<<p[pos2].c<<endl;
			int tem=query(p[pos2].c-1);
			if (~tem) dp[p[pos2].a]=max(dp[p[pos2].a], tem);
		}
		for (int i=l; i<pos1; ++i) del(p[i].c);
		sort(p+l, p+r+1, cmp1);
		cdq(mid+1, r);
	}
	void solve() {
		memset(dp, -1, sizeof(dp));
		memset(bin, -1, sizeof(bin));
		dp[0]=0;
		// for (int i=1; i<=n; ++i) uni[i]=a[i];
		// uni[n+1]=0;
		// sort(uni+1, uni+n+2);
		// usize=unique(uni+1, uni+n+2)-uni-1; lim=usize+3;
		lim=n+3;
		for (int i=1; i<=n; ++i) if (a[i]<=i) p[++tot].build(i, a[i]-i, a[i]+1);
		sort(p, p+n+1, cmp1);
		cdq(0, n);
		// cout<<"dp: "; for (int i=1; i<=n; ++i) cout<<dp[i]<<' '; cout<<endl;
		for (int i=1; i<=n; ++i) ans=max(ans, dp[i]);
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);

	n=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	// force::solve();
	// task1::solve();
	task2::solve();
	
	return 0;
}
上一篇:题解 客星璀璨之夜


下一篇:cdq实现树状数组