Question:
给出一个序列,我们每次都可以给一段子序列进行一次操作
序列的数据量是n(n<=1e5),我们每次进行n次操作在这个序列上,最后输出每个序列中的元素被操作的次数
Solution:
首先,为了更好的理解我们的线段树的优化措施
我们先来考虑一下我们的朴素的做法
对于这道题,我们的做法无非就是开辟一个一维数组,每次对那个区间进行操作,我们就对区间范围呢你的所有的数据都统一进行累加操作
最后,我们将以为数组的值全部输出就好了
该算法的时间复杂度是 O(n^2)
现在我们再来考虑线段树的做法,线段树的做法,在本体中应用了lazy标记的优化措施
我们的一旦发现了完全覆盖的区间就直接强行的退出这样我们就应用父区间完全代表子区间的方式成功的将算法单次操作的复杂度优化到O(logn)
总共有n此操作,所以说,insert操作的时间复杂度是就是O(n*logn)
之后我们还需要对结果继续累计
因为我们应用了lazy的思路,所以我们从根节点开始从上往下进行扫描,只要扫描到区间的计数器不是0,说明该区间范围内的所有节点曾经被至少共同操作过一次,我们累加
一层的该操作的时间复杂度是O(n)
总共有logn层,所以该操作的时间复杂度也是O(n*logn)
最后,线段树的操作的时间复杂度就是O(n*logn)
我们最后直观的来看一下,假如说计算机1s计算量为10^9
最坏的情况下,所有的操作都是对全部的区间进行的,
算法1的时间复杂度就是 1e5^2=10^10 > 10^9 超时显而易见
算法2的时间复杂度就是 1e5*lg1e5*2*c ~1e6 在大数据量的操作下,线段树的优势显而易见
Code:
#include"iostream"
#include"cstdio"
#include"cstdlib"
#include"cstring"
#define N 100005
using namespace std;
typedef struct node
{
int left;
int right;
int n;
}point;
int ans[N];
point tree[N*4];
int n;
void build(int left,int right,int i)
{
tree[i].left=left;
tree[i].right=right;
tree[i].n=0;
if(left==right) return ;
build(left,(left+right)>>1,i*2);
build(((left+right)>>1)+1,right,i*2+1);
}
void insert(int left,int right,int i)
{
if(tree[i].left==left&&tree[i].right==right)
{
tree[i].n++;
return ;
}
else
{
if(right<=tree[i*2].right) insert(left,right,i*2);
else if(left>=tree[i*2+1].left) insert(left,right,i*2+1);
else
{
insert(left,tree[i*2].right,i*2);
insert(tree[i*2+1].left,right,i*2+1);
}
}
}
void add(int i)
{
if(tree[i].n) for(int j=tree[i].left;j<=tree[i].right;j++) ans[j]+=tree[i].n;
if(tree[i].left==tree[i].right) return ;
add(i*2);
add(i*2+1);
}
int main()
{
int x,y;
while(scanf("%d",&n)&&n!=0)
{
memset(ans,0,sizeof(ans));
build(1,n,1);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
insert(x,y,1);
}
add(1);
for(int i=1;i<n;i++) printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
return 0;
}