Luogu T14448 区间开方

Posted 青石巷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu T14448 区间开方相关的知识,希望对你有一定的参考价值。

题面版权来自Shlw题目链接

题目背景

题目描述

给定一个数列,元素均为正整数,对其以下两种操作:

1.将某区间每一个数变为其算术平方根(取整)

2.求出某区间内所有数的最大值

输入输出格式

输入格式:

第一行包含两个整数n,m,分别表示该数列数字的个数和操作的总个数。第二行包含n个用空格分隔的整数,表示给定的数列。接下来m行,每行包含3个整数,表示一个操作,具体如下:

操作1:1 x y 含义:将区间[x,y]内每个数进行开方。

操作2:2 x y 含义:输出区间[x,y]内所有数的最大值。

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例#1:
9 7
71 25 69 41 75 91 12 76 24 
1 1 2
1 1 2
1 1 1
1 3 7
2 9 9
1 7 7
2 1 2
输出样例#1:
24
2

说明

对于20%的数据,n,m<=1000。

对于60%的数据,n,m<=10000。

对于100%的数据,n,m<=200000。

数据全部随机。

题解:

20分做法:

直接暴力。复杂度O(n*m)。

60分做法:

我不知道。网上有关于线段树处理区间开方区间求和的题,只是自己不会写,不知道能不能迁移到区间最值上。(其实是为了看起来有部分分才设置60%数据的)

100分做法:

注意到数据是较为随机的,也就是说m次操作中差不多有m/2次开方操作,区间随机时每个元素被开方m/4次。然而一个9位数开方几十次之后就会变成1,以后的所有开方操作都是毫无意义的。考虑到元素最小值为1,所以在查询最大值时也没有必要将1考虑进去。总而言之,1是“无需处理的元素”,然而在m次操作后期序列中会出现很长很长的一段全为1的元素,可以在区间操作时考虑跳过。

关于跳过一些未来可能会合并但一定不会分裂的区间可以使用并查集。

与之相似的一道题: CodeVS 4874 染色 题目链接

设fa[i]为“i以及i以后第一个不为1的数的编号”。换句话说,若a[i]不为1,则fa[i]=i;若a[i]为1,则fa[i]为i后第一个不为1的数的编号。发现fa可以进行路径压缩,在处理开方和求max时,从find(l)开始,逐次开方或求max,处理后若当前元素为1,将当前元素与下一个进行合并,否则开始处理下一个元素。

复杂度:O(跑得过)(手动滑稽)

代码:

技术分享
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=2e5+10;
 4 int a[maxn],fa[maxn];
 5 int n,m;
 6 int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
 7 void change(int l,int r)
 8 {
 9     int i,j;
10     i=l;
11     do
12     {
13         i=find(i);
14         if(i>r){break;}
15         a[i]=(int)(sqrt(a[i]));
16         if(a[i]==1){fa[i]=find(i+1);}
17         else{i++;}
18     }while(i<=r);
19     //for(i=1;i<=n;i++){cout<<a[i]<<" ";}cout<<endl;
20 }
21 void query(int l,int r)
22 {
23     int i=l,j,ans=1;
24     do
25     {
26         i=find(i);
27         if(i>r){break;}
28         ans=max(ans,a[i]);
29         if(a[i]==1){fa[i]=find(i+1);}
30         else{i++;}
31     }while(i<=r);
32     printf("%d\n",ans);
33 }
34 int main()
35 {
36     int i,j,flag,l,r;
37     //freopen("data.in","r",stdin);
38     //freopen("test.out","w",stdout);
39     cin>>n>>m;
40     for(i=1;i<=n;i++){scanf("%d",&a[i]);}
41     for(i=1;i<=n+1;i++){fa[i]=i;}
42     for(i=1;i<=m;i++)
43     {
44         scanf("%d%d%d",&flag,&l,&r);
45         if(flag==1){change(l,r);}
46         else{query(l,r);}
47     }
48     return 0;
49 }
View Code

 



以上是关于Luogu T14448 区间开方的主要内容,如果未能解决你的问题,请参考以下文章

Can you answer these queries?-HDU4027 区间开方

「SP2713」GSS4 - Can you answer these queries IV

线段树维护区间开方/除法

上帝造题的七分钟2/花神游历各国/GSS4 线段树维护区间开方 By cellur925

Tunnel Warfare(线段树 开方修改+剪枝优化

LOJ.6281.数列分块入门5(分块 区间开方)