首页 > 其他分享 >一道有趣的线段树题目

一道有趣的线段树题目

时间:2023-10-11 16:23:02浏览次数:35  
标签:题目 log min int 线段 MAXN 区间 有趣 复杂度

\(T4\) 莫队

image

image

image

首先我们需要知道一种统计答案的方法。

我们记 \(R_i\) 表示右边第一个和他相同的位置。

那么我们记 \(a_i=\min(a_{i+1},R_i)\) ,那么贡献就是 \(a_i-i+1\) ,所以我们最后就是要维护 \(a_i\) 就好了。

但是实际上如果你要直接维护 \(a_i\) 没有任何性质是做不了的。

所以我们只能考虑另外一种答案的贡献方式。

考虑一个 \(R_i\) 可以贡献给哪些位置。

显然是他前面一段以他 \(i\) 的后缀 \(\min\) 大于他的数,容易发现这个东西具有单调性,可以使用线段树二分,而对于合并子区间的时候,将右边区间的最小值丢到左边去,看一下能影响多少。对于小于他的数,就用原数,否则改成右区间的最小值。

具体实现,在 \(push\_up\) 的时候改一下就好了。

这样我们每个区间相当于是以当前区间的右端点开始后缀 \(\min\) 的贡献。

总时间复杂度 \(O(n\log^2 n)\)

启示:

关于要我们维护递增或递减关系的信息时,可以先对于每个子区间求出答案,然后在合并区间时再根据单调性用 \(\log\) 的复杂度更新,总复杂度 \(\log^2 n\) 每次操作。

点击查看代码
#include<bits/stdc++.h>
typedef long long LL;

using namespace std;
const int MAXN=2e5+10;
int n,m;
int a[MAXN],ys[MAXN];
set<int> s[MAXN];
struct data_structure {
	LL x,ans,mi;
}tr[(MAXN<<2)];
void ycl() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) {
		s[i].insert(n+1);
		scanf("%d",&a[i]);
		s[a[i]].insert(i);
	}
	for(int i=1;i<=n;++i) {
		auto it=s[a[i]].upper_bound(i);
		ys[i]=*it;
	}
}
LL merge(int u,int l,int r,LL x) {
	if(l==r) return min(tr[u].mi,x);
	int mid=(l+r)/2;
	if(tr[(u<<1|1)].mi<=x) return tr[u].ans-tr[(u<<1|1)].ans+merge((u<<1|1),mid+1,r,x);
	return (r-mid)*x+merge((u<<1),l,mid,x);
}
void psup(int u,int l,int mid,int r) {
	tr[u].mi=min(tr[(u<<1)].mi,tr[(u<<1|1)].mi);
	tr[u].ans=merge((u<<1),l,mid,tr[(u<<1|1)].mi)+tr[(u<<1|1)].ans;
}
void build(int u,int l,int r) {
	if(l==r) {
		tr[u].x=tr[u].mi=tr[u].ans=ys[l];
		return ;
	}
	int mid=(l+r)/2;
	build((u<<1),l,mid);
	build((u<<1|1),mid+1,r);
	psup(u,l,mid,r);
}
void update(int u,int l,int r,int x,int y) {
	if(l==r) {
		tr[u].x=tr[u].mi=tr[u].ans=y;
		return ;
	}
	int mid=(l+r)/2;
	if(x<=mid) update((u<<1),l,mid,x,y);
	else update((u<<1|1),mid+1,r,x,y);
	psup(u,l,mid,r);
}
LL ans,lim;
void query(int u,int l,int r,int x,int y) {
	if(l>y||r<x) return ;
	if(l>=x&&r<=y) {
		ans+=merge(u,l,r,lim);
		lim=min(lim,tr[u].mi);
		return ;
	}
	int mid=(l+r)/2;
	query((u<<1|1),mid+1,r,x,y);
	query((u<<1),l,mid,x,y);
}
int main () {
	freopen("team.in","r",stdin);
	freopen("team.out","w",stdout);
	ycl();
	build(1,1,n);
	for(int i=1;i<=m;++i) {
		LL opt,x,y;
		scanf("%lld%lld%lld",&opt,&x,&y);
		if(opt==1) {
			//del a_x
			s[a[x]].erase(x);
			auto it=s[a[x]].upper_bound(x);
			if(it!=s[a[x]].begin()) {
				--it;
				auto itt=s[a[x]].upper_bound(*it);
				update(1,1,n,*it,*itt);
			}
			//insert y
			a[x]=y;
			it=s[a[x]].upper_bound(x);
			if(it!=s[a[x]].end()) update(1,1,n,x,*it);
			if(it!=s[a[x]].begin()) {
				--it;
				update(1,1,n,*it,x);
			}
			s[a[x]].insert(x);
		}
		else {
			ans=0;
			lim=y+1;
			query(1,1,n,x,y);
			ans-=(x+y)*(y-x+1)/2;
			printf("%lld\n",ans);
		}
	}
	return 0;
}

标签:题目,log,min,int,线段,MAXN,区间,有趣,复杂度
From: https://www.cnblogs.com/ddl1no2home/p/17757480.html

相关文章

  • 有趣的概率——车羊问题与硬币问题
      1、经典车羊问题假设你参加一个游戏节目,有三扇关闭的门,其中一扇后面有一辆汽车,而其他两扇后面是山羊。你首先选择一扇门,然后主持人打开另外两扇门中的一扇,露出其中一只山羊。现在,你可以选择是否改变自己的选择,选择另外一扇未被打开的门。那么,应该改变选择还是保持原来的选......
  • 实现一个自动生成小学四则运算题目的命令行程序
    作业所属课程https://edu.cnblogs.com/campus/gdgy/CSGrade21-12/homework/13016作业要求https://edu.cnblogs.com/campus/gdgy/CSGrade21-12/homework/13016作业目标实现一个自动生成小学四则运算题目的命令行程序结对项目艾山·依力哈木+3120005145一......
  • 谈谈"求线段交点"的几种算法(js实现,完整版)
    谈谈"求线段交点"的几种算法(js实现,完整版)"求线段交点"是一种非常基础的几何计算,在很多游戏中都会被使用到. 下面我就现学现卖的把最近才学会的一些"求线段交点"的算法说一说,希望对大家有所帮助. 本文讲的内容都很初级,主要是面向和我一样的初学者,所以请各位算法帝......
  • [Резюме] 广义李超线段树
    前言“李超线段树,简称李超树。它支持插入直线或线段,并查询某个横坐标处的最值。”本文以最大值为例,讨论广义李超线段树,与传统的李超线段树不同,它插入的是函数而非直线或线段,但为了保证正确的时间复杂度,对函数有较严格限制,见条件。不展开讨论对于\(x=k\)时函数值相等时......
  • 题目集1-3的总结java
    题目集1-3的总结java21207218-石子颖一.前言    题目一是我刚接触java代码后的第一次练习,题目量有点多,但是都不太难,加上有之前的c语言的基础,这次只需要掌握一些java基本语法和利用面向过程的基本思维,只需要写一个类便可以直接按照题目所给的逻辑将代码简单写出,只有在最......
  • 题目集1-3的总结
    一.前言第一次题目比较简单,题目量偏多,主要是基本的程序设计和基本语法操作。这次题目包含的知识点有:if()选择结构、Java的输入对象的建立、for()循环的使用、Java的控制台输出、字符串的相关函数。第二次的题目量和第一次一样,难度适中,此次题目中包含的知识点有:定义类和创建对象、字......
  • nchu题目集1~3的总结性Blog
    题目集1~3的总结性Blog 一、前言:总结三次题目集的知识点、题量、难度等情况  针对在完成三次作业过程中产生的问题进行总结与分析。三次作业一共二十一道题目,其中前两次难度较为简单,第三次难度陡然攀升。题量方面一直处于量大管饱的阶段,尤其是第三次作业,当我满心欢喜的......
  • 前三次题目总结
    前三次题目总结前言1.知识点(1)第一次题目集:简单类的创建,java.util.Scanner方法的运用,System.out.println、System.out.print、System.out.printf三种不同输出的区别,C语言中一些简单的ifelse条件判断语句,for循环,switch选择语句,加减乘除等运算。(2)第二次题目集:类、数组的基本......
  • pta三次题目集总结
    一、前言      第一次题目集,一共九道题目,题目量虽然有点大,但是都是一些基础题,涉及基础的输入和输出、浮点数的应用、日常加减乘除运算、字符串的应用、提取所需信息等。都是一些简单基础题。      第二次题目集,一共八道题,题目量也是比较多的,这一次相对于第一次难......
  • 前三次题目总结
    前三次题目总结一、前言1.知识点第一次作业目比较基础,主要包含输入与输出,以及一些简单的计算题目和判断类别,关键点及难点在于输入数据的格式使用是String还是double或者是float,其中double和float最容易混淆,主要在于所需的精确度;还有printf以及println输出的区别,看题目具体要求,......