题解 [PR #1] 删数

传送门

  • 见到 \(a_{i-1}, a_{i+1}\) 先想想放到差分序列上有什么性质

自闭了,签到题不会做

考虑差分序列 \(d_i=a_i-a_{i-1}\)
那么每次就是选两个相邻且相等的 \(d_i, d_{i-1}\) 合并成 \(d_i+d_{i-1}\)
应当注意到的性质是这一过程只能重复 \(\log V\)
而且只有符号相同且 \(\frac{d_i}{\operatorname{lowbit}(d_i)}\) 相等的 \(d_i\) 才能合并
那么可以据此将差分序列分为若干个连续段,每个连续段间是独立的
发现两个相等值相加等价于 ×2
那么问题转化为给定一个序列 a,\(a_i\in[0, 32]\),每次可以选两个相等的数 \(v\) 合并成 \(v+1\),求最大合并次数
还是根据只能合并 log 次,一个 DP 是令 \(g_{i, j}\) 为右端点为 \(i\) 时,合并出 \(j\) 的左端点
\(f_i\) 为前 \(i\) 个位置的答案,那么可以利用 \(g\) 来转移 \(f\)
注意特殊处理差分序列中的 0
复杂度 \(O(n\log V)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 300010
#define ll long long
//#define int long long

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], b[N], d[N], tag[N], f[N], g[N][35], pre[N], lg[N], tot, ans;
inline int lowbit(int i) {return i&-i;}

int solve() {
	// cout<<"solve: "; for (int i=1; i<=tot; ++i) cout<<d[i]<<' '; cout<<endl;
	for (int i=1; i<=tot; ++i) for (int j=0; j<=32; ++j) g[i][j]=0;
	for (int i=1; i<=tot; ++i) pre[i]=pre[i-1]+(1<<d[i])-1, f[i]=0;
	for (int i=1; i<=tot; ++i) {
		g[i][d[i]]=i;
		for (int j=d[i]+1; j<=32&&g[i][j-1]; ++j)
			g[i][j]=g[g[i][j-1]-1][j-1];
		for (int j=d[i]; j<=32&&g[i][j]; ++j)
			f[i]=max(f[i], f[g[i][j]-1]+(1<<j)-1-pre[i]+pre[g[i][j]-1]);
		f[i]=max(f[i], f[i-1]);
	}
	// cout<<g[2][1]<<endl;
	// cout<<"f: "; for (int i=1; i<=tot; ++i) cout<<f[i]<<' '; cout<<endl;
	return f[tot];
}

signed main()
{
	int T=read();
	while (T--) {
		ans=n=read();
		for (int i=1; i<=n; ++i) a[i]=read();
		for (int i=2; i<=n; ++i) d[i]=a[i]-a[i-1];
		// cout<<"d: "; for (int i=2; i<=n; ++i) cout<<d[i]<<' '; cout<<endl;
		for (int i=2; i<=n; ++i)
			if (d[i]) tag[i]=(d[i]<0?-1:1)*abs(d[i])/lowbit(abs(d[i]));
			else tag[i]=0;
		// cout<<"tag: "; for (int i=2; i<=n; ++i) cout<<tag[i]<<' '; cout<<endl;
		int lst=2, min_tag;
		for (int i=2; i<=n; ++i) {
			if (tag[i]!=tag[lst]) {
				tot=0; min_tag=INF;
				for (int j=lst; j<i; ++j) min_tag=min(min_tag, abs(a[j]-a[j-1]));
				if (!min_tag) ans-=max(0, i-lst-1);
				else {
					for (int j=lst; j<i; ++j) d[++tot]=__builtin_ctz(abs(a[j]-a[j-1])/min_tag);
					ans-=solve();
				}
				lst=i;
			}
		}
		tot=0; min_tag=INF;
		for (int i=lst; i<=n; ++i) min_tag=min(min_tag, abs(a[i]-a[i-1]));
		if (!min_tag) ans-=max(0, n-lst+1-1);
		else {
			for (int i=lst; i<=n; ++i) d[++tot]=__builtin_ctz(abs(a[i]-a[i-1])/min_tag);
			ans-=solve();
		}
		printf("%d\n", ans);
	}

	return 0;
}
posted @ 2022-04-06 18:53  Administrator-09  阅读(24)  评论(0编辑  收藏  举报