【题解】替换/子数组

首先是本次测试总结:

T2是练习的原题,这道题我刚好练习时没做,太巧了。

T3我很快看出来从小到大排序就是答案(逆序对为0),后来暴力不过,于是从选第N+1条裤子递推到了选第一条裤子,再重新输出。不过用一个数组记录后面裤子的前缀,一个数组记录前面裤子的前缀(不论选哪条裤子都适用),就很简单了。这就是记忆化思想,包括昨天的题。(我最后半小时才想到一个复杂的方法,太惊险。)

T4是二分+贪心,由MID来决定I的选择,判定答案往往能贪心,因为它只关注的是MID能否成立,更好操作。就像知道了已知条件,比直接求解问题更简单。

继续努力,向大佬们看齐OR2!


A. 替换

对于一个大于1的整数X,,你可以进行以下替换操作:

删掉它,把它换成三个数:[X/2],[X%2],[X/2]。

现在给你一个数列,开始数列只有一个整数N,你每次可以在数列中选择一个大于1的整数进行上述操作,直到这个数列最后全是1或0为止。

接下来,在最后的数列中查询区间[L,R]中有多少个1。输出1的个数。


解析:我们发现对于一个数N,会被拆分成两部分,这两部分是对称的。但难点在于它只告诉我们求最终序列所给区间的1,似乎必须把整个数组递归模拟下来才行。而且1似乎也没什么特点,可以是每次中间余的数,也可以是最终拆分成的数。

所以我们必须从最终序列入手,再缩小规模,直至操作结束。

再往下分析,我们可以求出最终序列长len:

len=1;
for(ll i=n>>1;i;i>>=1) {
	len=len*2+1;
}

定义函数s(len,l,r,x)表示在数x所形成序列中L~R区间1的个数,len表示序列长度。由于最终序列左右一定具有对称性,我们可以根据l,r的位置把序列分割成左右两块,在更小的序列中分别求值,再累加返回。

没错,这就是:分治!

尽管这个区间横跨左右部分,仍可以强行分成两部分解决。

ll flag=0,t=len/2+1;
if(n==1) return 1;
if(n==0||l>r) return 0;
if(l<=t&&r>=t&&n%2==1) flag=1;
return search(len/2,l,t-1,n/2)+search(len/2,1,r-t,n/2)+flag;

【标签】分治


B. 子数组

给出一个N行N列的二维数组,元素只取0,1,2这三种值,并且每行、每列的元素值单调不下降。在此数组中,找出一个值全部等于0或者全部等于2的子二维数组,使得这个子数组的元素个数最多。子二维数组是指行数、列数不超过N的连续的一个矩形区域。

样例输入
8
4 0
4 8
4 8
3 7
3 6
3 5
2 3
0 2

样例输出
12
4

样例说明
输入数据描述的二维数组是这样的:
0 0 0 1 1 1 1 1
0 0 0 1 1 1 1 2
0 0 0 1 1 1 1 2
0 0 1 1 1 1 2 2
0 0 1 1 1 2 2 2
0 0 1 1 2 2 2 2
0 1 2 2 2 2 2 2
0 2 2 2 2 2 2 2
可以看出,一共有4个值全部相同的子二维数组,分别是:(1,1)到(6,2);(5,6)到(8,8); (7,3) 到 (8,8); (6,5)到(8,8).

解析:既然这道题是求0和2的最大矩形,你就明白了。0和2显然比1规则,根据输入处理每行的0,2个数,再找个最大矩阵就好了,因为一边是对齐的,所以很好操作

引入:单调栈(不用也行)

大致思想是:1~n每次进入一行0(以0举例),设栈s[],如果个数大于栈顶,直接进栈;否则不断取出栈顶,直至栈为空或者栈顶矩形高度比当前小,我们累计被弹出矩形宽度之和,借此更新答案,结束后,把一个高度为当前矩阵·宽度为累加值的新矩形入栈。

//当前内容引自算法竞赛
for(int i=1;i<=n;i++) {
	if(a[i]>s[p]) {
		s[++p]=a[i],w[p]=1;
	}
	else {
		int width=0;
		while(s[p]>a[i]) {
			width+=w[p];
			ans=max(ans,(long long)width*s[p]);
			p--;
		}
		s[++p]=a[i],w[p]=width+1;
	}
}

这只是一种思想,只会打是没有用的。如(s[p] > a[ i ]),不一定要严格大于,若有高度相同的矩阵,也应该弹出,还有边界设定,都很讲究。

(完)

好累啊,点个赞呗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值