周子衡
2019-01-31 10:01:43
先来一个小问题:
有N个数,M次询问,每次给定区间[L,R],求区间内的最大值。
N<=10,M<=10
老师,我会
再来:
N<=10^5,M<=10^5
老师,我会线段树
再来:P3865 【模板】ST表
N<=10^5,M<=10^6
这时我们发现,随着
倍增算法(例题:P3379 【模板】最近公共祖先(LCA))
区间动规(例题:P3146 [USACO16OPEN]248)
我们现在要
但是这样预处理是
观察到一个性质:
计算机中有很多事物是跟
这一条非常重要,我们画个图理解一下:
现在我们考虑
我们把
我们发现,在这种方式下,以每个点为起点都有
那怎么处理询问呢?
根据
记询问区间长度为
当然为了保证询问复杂度为
顺便附上代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100001]={};
int lg[100001]={-1};
int maxn[100001][50]={};
int main()
{
int n=0,m=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
lg[i]=lg[i/2]+1;
}
for(int i=1;i<=n;i++)
{
maxn[i][0]=a[i];
}
for(int i=1;i<=lg[n];i++)
{
for(int j=1;j+(1<<i)-1<=n;j++)
{
maxn[j][i]=max(maxn[j][i-1],maxn[j+(1<<(i-1))][i-1]);
}
}
int l=0,r=0;
while(m--)
{
scanf("%d%d",&l,&r);
int len=lg[r-l+1];
printf("%d\n",max(maxn[l][len],maxn[r-(1<<(len))+1][len]));
}
return 0;
}
先来一道模板题:P2880 [USACO07JAN]平衡的阵容Balanced Lineup
给定N个数和M个询问,求每次询问区间内极差=最大值-最小值。
用
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100001]={};
int lg[100001]={-1};
int maxn[100001][50]={};
int minn[100001][50]={};
int main()
{
int n=0,m=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
lg[i]=lg[i/2]+1;
maxn[i][0]=a[i];
minn[i][0]=a[i];
}
for(int i=1;i<=lg[n];i++)
{
for(int j=1;j+(1<<i)-1<=n;j++)
{
maxn[j][i]=max(maxn[j][i-1],maxn[j+(1<<(i-1))][i-1]);
minn[j][i]=min(minn[j][i-1],minn[j+(1<<(i-1))][i-1]);
}
}
int l=0,r=0;
while(m--)
{
scanf("%d%d",&l,&r);
int len=lg[r-l+1];
printf("%d\n",max(maxn[l][len],maxn[r-(1<<len)+1][len])-
min(minn[l][len],minn[r-(1<<len)+1][len]));
}
return 0;
}
当然,
于是就有了这道新鲜出炉的省选题:(
给定N个整数和M个询问,每次询问给定一个X,求有多少个区间[L,R]使得A[L]~A[R]的GCD为X。
算法1:
老师,我会暴力枚举每一个区间求
复杂度:
期望得分:
算法2:(本蒟蒻的做法)
老师,我会把所有区间
复杂度:
期望得分:
算法3:
显然
然后每个询问
接着我们发现,以每个数为起点的区间
我们可以二分出第一次变化的点,记录出现次数,插入
复杂度:
期望得分:
thanks to @xhhkwy
假设现在有个毒瘤出题人故意卡你……
有N个数,M次询问,每次给定区间[L,R],求区间内的最大值。
N<=2*10^7,M<=2*10^7,随机数据,时限5s
然后发现……预处理都
怎么办?我们要想方设法降低
分块都学过吧(没学过?出门左转P1903 [国家集训队]数颜色 / 维护队列)
让我们瞻仰一下@xhhkwy的仙气
将序列分成长度是logN的块,预处理出每一块的前缀min与后缀min,
然后在把每一个块的最小值拉出来跑st,
预处理时间复杂度为N + (N/logN)*log(N/logN) = O(N),
询问的话如果两个端点在一个块中那么暴力,时间复杂度O(logN)。
否则直接查st表+前后缀min
(引自@xhhkwy私信)
各位自行理解吧,注意只有数据随机的情况才能使大部分查询操作复杂度为
本蒟蒻的
点个赞呗