同时维护线段树区间最小值和最小值的数量
code
#include <iostream>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <algorithm>
#define forn(i, n) for (int i = 0; i < int(n); i++)
using namespace std;
inline int read(){
int x = 0, op = 1; char ch = getchar();
while (!isdigit(ch)){ if (ch == '-') op = -1; ch = getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48), ch = getchar();}
return x * op;
}
const int N = 1e5 + 100;
int a[N];
struct tree{
int l, r;
int mx;
int cnt;
int mid(){
return (l + r) >> 1;
}
}t[N << 2];
inline void push(int rt){
t[rt].mx = min(t[rt << 1].mx, t[rt << 1|1].mx);
if (t[rt << 1].mx == t[rt << 1|1].mx)
{
t[rt].cnt = t[rt << 1].cnt + t[rt << 1|1].cnt;
}else if(t[rt << 1].mx < t[rt << 1|1].mx){
t[rt].cnt = t[rt << 1].cnt;
}else{
t[rt].cnt = t[rt << 1|1].cnt;
}
}
struct item
{
int m, num;
};
inline void build(int rt, int l, int r){
t[rt].l = l, t[rt].r = r;
if (l == r)
{
t[rt].mx = a[l];
t[rt].cnt = 1;
return;
}
int mid = t[rt].mid();
build(rt << 1, l, mid);
build(rt << 1|1, mid + 1, r);
push(rt);
}
inline void update(int rt, int pos, int val){
if (t[rt].l == t[rt].r)
{
t[rt].mx = val;
return;
}
int mid = t[rt].mid();
if (pos <= mid)
{
update(rt << 1, pos, val);
}else{
update(rt << 1|1, pos, val);
}
push(rt);
}
item merge(item x, item y){
if (x.m < y.m) return x;
else if (x.m > y.m) return y;
else return {x.m, x.num + y.num};
}
item query(int rt, int lb, int rb){
item res = {INT_MAX, 0};
if (t[rt].l >= lb && t[rt].r <= rb)
{
return item{t[rt].mx, t[rt].cnt};
}
int mid = t[rt].mid();
if (mid >= lb)
{
res = merge(res, query(rt << 1, lb, rb));
}
if (mid < rb)
{
res = merge (res, query(rt << 1|1, lb, rb));
}
return res;
}
int n, m;
int main(int argc, char const *argv[])
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
{
a[i] = read();
}
build(1, 1, n);
while(m--){
int op = read(), x = read(), y = read();
if (op == 1)
{
update(1, x + 1, y);
}else{
item ans = query(1, x + 1, y);
printf("%d %d\n", ans.m, ans.num);
}
}
return 0;
}