题解 [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;
}