首先是本次测试总结:
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 ]),不一定要严格大于,若有高度相同的矩阵,也应该弹出,还有边界设定,都很讲究。
(完)
好累啊,点个赞呗