删数

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;
}
posted @ 2022-04-04 10:42  FxorG  阅读(25)  评论(0编辑  收藏  举报