专栏文章
P11283 题解
P11283题解参与者 3已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @mir2kszj
- 此快照首次捕获于
- 2025/12/04 14:43 3 个月前
- 此快照最后确认于
- 2025/12/04 14:43 3 个月前
1. 题目分析
首先简化一下肿胃数的定义,每次任意选连续的三个数,删除其中的中位数,在 次操作后,剩下的三个数的中位数的最大值即是肿胃数。
其实我们只需要看有哪些区间中的最大值一定可以作为最终的答案,从中取所有的最大值即可。
其实我们只需要看有哪些区间中的最大值一定可以作为最终的答案,从中取所有的最大值即可。
判断哪些区间中最大值可以作为最终答案
首先对于总区间,其最大值和最小值都不能作为最终答案,这是显然的。
接下来我们通过手推发现以下区间中的最大值是可以作为最终答案的(我们用 表示总区间最大值,用 表示在总区间最大值的左边和右边的区间中更大的最小值,用 表示在总区间最大值左边和右边的区间中更小的最小值):

证明:
对于两个红色区间中的最大值,因为 或 都小于它们,所以如果只在红色区间操作的话,红色区间中除了最大值外的数一定可以作为中位数被全部删完,故红色区间中的最大值可以被保留。
而对于绿色和蓝色区间,因为它们中的最大值又小于 ,最小值又大于 或 ,所以一定可以全部作为中位数被删去。
接下来就只剩左红色区间最大值、、、、右红色区间最大值。现在若我们想要保留左红色区间最大值,那么我们可以先选择右边三个数操作将右红色区间最大值删去,再选择两个最小值和总区间的最大值,将 删去,最后得到最终答案为左红色区间最大值。保留右红色区间最大值的操作是保留左红色区间最大值的镜像操作。
故两个红色区间的最大值都可以被保留。
证毕。
简单推一下,我们发现可以先将左右红色区间最大值先删去,所以 也可以被保留, 也可能是肿胃数,大家不要遗漏了。
那么就只有这两个区间的最大值可以被保留吗?答案是否定的。
设在 右边第一个小于 的数为 ,那么区间 到 中的最大值是可以被保留的。
证明:
因为 到 的右边有蓝色和右红色区间的共同最小值 ,所以我们可以在不删去 的情况下将此区间中的最大值保留下来。至于在 左边的蓝色区间则直接用 和 全部删掉即可。
我这里用 表示 到 的最大值(假设最大值不是 ,其实后面你仔细想想如果最大值是 也没影响),用 表示右红色区间最大值,左红色区间最大值很显然可以被删掉就不表示了。
最后情况的处理如下(这里保证 ):

如果 呢,那么请问,如果 ,你为什么要选 而不选更大的 呢,对吧。
证毕。
对于其他区间中的数,因为有 和 ,所以无论怎样都不会成为最终答案的,会直接被当成中位数删掉。
至于 和 的位置交换之后也一样。
如果没有 的话,我们可以直接取 为 或 (根据 在 的左边还是右边而定)。
接下来我们通过手推发现以下区间中的最大值是可以作为最终答案的(我们用 表示总区间最大值,用 表示在总区间最大值的左边和右边的区间中更大的最小值,用 表示在总区间最大值左边和右边的区间中更小的最小值):

证明:
对于两个红色区间中的最大值,因为 或 都小于它们,所以如果只在红色区间操作的话,红色区间中除了最大值外的数一定可以作为中位数被全部删完,故红色区间中的最大值可以被保留。
而对于绿色和蓝色区间,因为它们中的最大值又小于 ,最小值又大于 或 ,所以一定可以全部作为中位数被删去。
接下来就只剩左红色区间最大值、、、、右红色区间最大值。现在若我们想要保留左红色区间最大值,那么我们可以先选择右边三个数操作将右红色区间最大值删去,再选择两个最小值和总区间的最大值,将 删去,最后得到最终答案为左红色区间最大值。保留右红色区间最大值的操作是保留左红色区间最大值的镜像操作。
故两个红色区间的最大值都可以被保留。
证毕。
简单推一下,我们发现可以先将左右红色区间最大值先删去,所以 也可以被保留, 也可能是肿胃数,大家不要遗漏了。
那么就只有这两个区间的最大值可以被保留吗?答案是否定的。
设在 右边第一个小于 的数为 ,那么区间 到 中的最大值是可以被保留的。
证明:
因为 到 的右边有蓝色和右红色区间的共同最小值 ,所以我们可以在不删去 的情况下将此区间中的最大值保留下来。至于在 左边的蓝色区间则直接用 和 全部删掉即可。
我这里用 表示 到 的最大值(假设最大值不是 ,其实后面你仔细想想如果最大值是 也没影响),用 表示右红色区间最大值,左红色区间最大值很显然可以被删掉就不表示了。
最后情况的处理如下(这里保证 ):

如果 呢,那么请问,如果 ,你为什么要选 而不选更大的 呢,对吧。
证毕。
对于其他区间中的数,因为有 和 ,所以无论怎样都不会成为最终答案的,会直接被当成中位数删掉。
至于 和 的位置交换之后也一样。
如果没有 的话,我们可以直接取 为 或 (根据 在 的左边还是右边而定)。
2. 题目做法
对于这道题,我们需要求出 、、、 的位置。
- 暴力,。
每次询问 暴力扫,不过多阐述。 - 线段树加二分,。
预处理 ,每次询问可以 求出 、、。 对于 ,可以在 和 之间二分,二分中每次都要调用线段树查区间最小值,故每次查询要消耗 。 - st 表加二分,。
预处理 ,每次询问 求出 、、。
对于 ,二分方法和线段树做法一样,只不过每次查区间最小值是 的,所以每次查询只消耗 。 - st 表加单调栈,。
我这人写的代码常数比较大,上面的做法我的代码都过不了,所以只能用这种做法过。
时间复杂度瓶颈在于求 ,但我们发现在 到 之间没有 的数,也就是 是在 的左边或右边的第一个 的数,这样很容易想到用单调栈线性 预处理,这样每次查 也变成 了。
3. 代码
CPP#include<bits/stdc++.h>
#define ll long long
//#define getchar getchar_unlocked
//#define putchar putchar_unlocked
using namespace std;
int n,m;
unsigned seed,A,B,C;
inline unsigned rnd() {
seed=A*seed*seed+B*seed+C;
seed=seed^(seed<<27);
seed=seed^(seed>>19);
return seed;
}
int l,r;
inline void gen() {
do {
l = rnd() % n + 1;
r = rnd() % n + 1;
} while (abs(l - r) < 2);
if(l>r)
l^=r^=l^=r;
}
const int N=1000010,M=20;
inline unsigned read()
{
unsigned x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
inline int max(int x,int y)
{
return x>y?x:y;
}
int a[N];
int w[N],mx[M][N],mn[M][N];
void init()
{
for(int i=1;i<=n;i++)
mx[0][i]=mn[0][i]=i;
for(int i=1;i<=w[n];i++)
{
int t1=n-(1<<i)+1,t2=1<<i-1;
for(int j=1;j<=t1;j++)
{
mx[i][j]=a[mx[i-1][j]]>a[mx[i-1][j+t2]]?mx[i-1][j]:mx[i-1][j+t2];
mn[i][j]=a[mn[i-1][j]]<a[mn[i-1][j+t2]]?mn[i-1][j]:mn[i-1][j+t2];
}
}
}
inline int qmx(int l,int r)
{
if(l>r)
return 0;
int t1=w[r-l+1],t2=(1<<t1)-1;
return a[mx[t1][l]]>a[mx[t1][r-t2]]?mx[t1][l]:mx[t1][r-t2];
}
inline int qmn(int l,int r)
{
if(l>r)
return 0;
int t1=w[r-l+1],t2=(1<<t1)-1;
return a[mn[t1][l]]<a[mn[t1][r-t2]]?mn[t1][l]:mn[t1][r-t2];
}
stack<int>s;
int l1[N],r1[N];
ll ans;
int main()
{
n=read(),m=read(),seed=read(),A=read(),B=read(),C=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=2;i<=n;i++)
w[i]=w[i>>1]+1;
init();
a[0]=INT_MAX;
for(int i=1;i<=n;i++)
{
while(!s.empty()&&a[s.top()]>a[i])
r1[s.top()]=i,s.pop();
s.push(i);
}
while(!s.empty())//记得清空!
s.pop();
for(int i=n;i;i--)
{
while(!s.empty()&&a[s.top()]>a[i])
l1[s.top()]=i,s.pop();
s.push(i);
}
for(ll i=1;i<=m;i++)
{
gen();
int zmx=qmx(l,r),lmn=qmn(l,zmx-1),rmn=qmn(zmx+1,r);
if(a[lmn]<a[rmn])
rmn?lmn=l1[rmn]:lmn=zmx-1;
else if(a[lmn]>a[rmn])
lmn?rmn=r1[lmn]:rmn=zmx+1;
ans^=i*max(lmn?a[qmx(l,lmn)]:0,rmn?a[qmx(rmn,r)]:0);
}
printf("%lld",ans);
return 0;
}
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...