NOIP T3!!!序列先看看差分数组!!!
A. 删数
Solution
考虑操作本质上就是合并 2 个相邻且相同的差分数组,那么对于一个差分值 $x$,合成到最后一定是 $x*2^k$,考虑 dp,设 $f[i]$ 表示差分数组 $[1,i]$ 最少留下几个数,考虑以 $i$ 倍增去跳,即 $map[i][a[i]]$ 表示以 i 为右端点合并到最后的差分值是 $a[i]$ 所在的左端点(开)。用 map 记录下即可。http://pjudge.ac/submission/25713
#include <bits/stdc++.h>
using namespace std;
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
const int N=(int)(3e5+5);
map<int,int>mp[N];
int n,a[N],d[N],f[N];
void solve() {
n=rd(); for(int i=1;i<=n;i++) a[i]=rd();
for(int i=1;i<n;i++) d[i]=a[i+1]-a[i];
--n; for(int i=0;i<=n;i++) f[i]=(int)(2e9),mp[i].clear();
f[0]=0;
for(int i=1;i<=n;i++) {
int y=i-1; int x=d[i];
mp[i][x]=i-1; //这里是开的
f[i]=f[i-1]+1;
while(1) {
auto qwq=mp[y].find(x);
if(qwq==mp[y].end()) break ;
y=(*qwq).second; x*=2;
f[i]=min(f[i],f[y]+1);
mp[i][x]=y;
}
} // f[i] 代表的是 差分数组 [1,i] 最少剩下多少个,所以最后原序列答案+1
printf("%d\n",f[n]+1);
}
signed main() {
int T=rd(); while(T--) solve(); return 0;
}