暑假集训 ---- 数据结构

CF377D Developing Game
考虑到一个合法的方案必须有 m a x ( l i ) ≤ m i n ( v i ) max(l_i)\le min(v_i) max(li)min(vi) m a x ( v i ) ≤ m i n ( r i ) max(v_i)\le min(r_i) max(vi)min(ri)
也就是一定存在一个 ( L , R ) (L,R) (L,R)使得 L ∈ [ m a x ( l i ) , m i n ( v i ) ] , R ∈ [ m a x ( v i ) , m i n ( r i ) ] L\in [max(l_i),min(v_i)],R\in[max(v_i),min(r_i)] L[max(li),min(vi)],R[max(vi),min(ri)]
发现 ( L , R ) (L,R) (L,R) 对应一个坐标,而 l i , v i , r i l_i,v_i,r_i li,vi,ri对应一个矩阵,扫描线即可
CF464E The Classic Problem
看看最短路需要支持哪些操作:
1.赋值
2.比较两个数 ---- 找到最高的不同位置
3.给一个数加上 2 x 2^x 2x ---- 找到第一个 > = x >= x >=x且为 0 的位置 p p p,把 [ x , p − 1 ] [x,p-1] [x,p1]赋成0,把 p p p 赋成 1
发现可以用主席树实现

#include<bits/stdc++.h>
#define N 200000
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m, first[N], nxt[N<<1], to[N<<1], w[N<<1], tot;
void add(int x, int y, int z){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
typedef long long ll;
const int Base = 2, Mod = 1e9 + 7;
const int up = 150000;
ll Mul[N]; int st, ed, rt[N], idx;
struct President{
	int ls, rs; ll sum;
}t[N * 100];
int modify(int last, int &x, int l, int r, int pos){
	x = ++idx; t[x] = t[last];
	if(l == r){ t[x].sum = t[last].sum ^ 1; return t[last].sum;}
	int mid = (l+r) >> 1, res = 0;
	if(pos <= mid){
		res = modify(t[last].ls, t[x].ls, l, mid, pos);
		if(res) res = modify(t[last].rs, t[x].rs, mid+1, r, pos);
	}
	else res = modify(t[last].rs, t[x].rs, mid+1, r, pos);
	t[x].sum = (t[t[x].rs].sum * Mul[mid-l+1] % Mod + t[t[x].ls].sum) % Mod; return res;
}
bool Comp(int a, int b, int l, int r){
	if(l == r) return t[a].sum > t[b].sum;
	int mid = (l+r) >> 1;
	if(t[t[a].rs].sum == t[t[b].rs].sum) return Comp(t[a].ls, t[b].ls, l, mid);
	else return Comp(t[a].rs, t[b].rs, mid+1, r);
} 
int from[N]; bool vis[N];
struct Node{
	int x, rt;
	Node(int _x = 0, int _rt = 0){ x = _x; rt = _rt;}
	bool operator < (const Node &a) const{ return Comp(rt, a.rt, 0, up);}
}; priority_queue<Node> q;
void Print(int x, int dep){
	if(x == st){ printf("%d\n%d ", dep, x); return;}
	Print(from[x], dep+1); printf("%d ", x);
}
void Dijsktra(){
	q.push(Node(st, rt[st]));
	while(!q.empty()){
		int x = q.top().x, rot = q.top().rt; q.pop();
		if(vis[x]) continue; vis[x] = true;
		if(rt[x] != rot) continue;
		if(x == ed){ printf("%lld\n", t[rt[ed]].sum); Print(x, 1); return;}
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; int now = 0; modify(rt[x], now, 0, up, w[i]);
			if(!rt[t] || Comp(rt[t], now, 0, up)){
				from[t] = x;
				rt[t] = now; q.push(Node(t, rt[t])); 
			}
		}
	} puts("-1");
}
int main(){
	n = read(), m = read();
	Mul[0] = 1;
	for(int i = 1; i <= up; i++) Mul[i] = Mul[i-1] * Base % Mod;
	for(int i = 1; i <= m; i++){
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
	} st = read(), ed = read();
	Dijsktra(); return 0;
}

CF475F Meta-universe
一种暴力的做法就是按 x x x y y y排序,扫 x , y x, y x,y,然后扫到了就切成两边,但这样最坏是 O ( n 2 ) O(n^2) O(n2)
任然用暴力的做法,考虑到用小的集合的大小就可以将一个大大的集合分成一个小集合和一个大集合
有些类似启发式合并用小集合的大小可以合并成一个大大的集合,于是复杂度就是 O ( n l o g n 2 ) O(nlogn^2) O(nlogn2)
不妨称之为启发式分裂

#include<bits/stdc++.h>
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
struct Point{ int x, y; };
struct cmp1{ bool operator () (const Point &a, const Point &b){ return a.x < b.x || (a.x == b.x && a.y < b.y); }};
struct cmp2{ bool operator () (const Point &a, const Point &b){ return a.y < b.y || (a.y == b.y && a.x < b.x); }};
typedef set<Point, cmp1> Sx;
typedef set<Point, cmp2> Sy;
typedef Sx::iterator Itx;
typedef Sy::iterator Ity;
Sx sx; Sy sy;
int n, ans;
void Split(Sx &S1, Sy &S2){
	Itx lx = S1.begin(), rx = --S1.end();
	Ity ly = S2.begin(), ry = --S2.end();
	int n = S1.size() >> 1;
	for(int i = 1; i <= n; i++){
		Itx tp = lx++;
		if(tp->x + 1 < lx->x){
			Sx S3; Sy S4; lx = ++tp;
			for(tp = S1.begin(); tp != lx;){
				Itx now = tp++;
				S3.insert(*now);
				S4.insert(*now);
				S2.erase(*now);
				S1.erase(now);
			} Split(S1, S2); Split(S3, S4);
			return;
		}
		tp = rx--;
		if(rx->x + 1 < tp->x){
			Sx S3; Sy S4; rx = --tp;
			for(tp = --S1.end(); tp != rx;){
				Itx now = tp--;
				S3.insert(*now);
				S4.insert(*now);
				S2.erase(*now);
				S1.erase(now);
			} Split(S1, S2); Split(S3, S4);
			return;
		}
		tp = ly++;
		if(tp->y + 1 < ly->y){
			Sx S3; Sy S4; ly = ++tp;
			for(tp = S2.begin(); tp != ly;){
				Ity now = tp++;
				S3.insert(*now);
				S4.insert(*now);
				S2.erase(now);
				S1.erase(*now);
			} Split(S1, S2); Split(S3, S4);
			return;
		}
		tp = ry--;
		if(ry->y + 1 < tp->y){
			Sx S3; Sy S4; ry = --tp;
			for(tp = --S2.end(); tp != ry;){
				Ity now = tp--;
				S3.insert(*now);
				S4.insert(*now);
				S2.erase(now);
				S1.erase(*now);
			} Split(S1, S2); Split(S3, S4);
			return;
		}
	} ++ans;
}
int main(){
	n = read();
	while(n--){
		int x = read(), y = read(); Point now = (Point){x, y};
		sx.insert(now); sy.insert(now);
	} Split(sx, sy); cout << ans; return 0;
}

CF643G Choosing Ads
考虑到绝对众数的 O ( n ) O(n) O(n) 做法,就是维护一个桶与当前的数,如果加入的不是当前数,个数–,反之个数++,如果个数 < 0 <0 <0就把数换成当前数,显然出现次数超过一半的众数是不会被减完的
这道题也同理,我们维护 5 个桶,一个区间的众数要么是它左半边的 5 个,要么是它右半边的 5 个,线段树 p u s h u p pushup pushup 的时候按照上面的思路加加减减即可

#include<bits/stdc++.h>
#define N 150050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m, p;
int a[N];
struct tree{
	int num, tag, a[5], b[5];
	tree operator + (const tree &A){
		tree res = A; res.tag = 0;
		for(int i = 0; i < num; i++){
			bool flag = 0;
			for(int j = 0; j < res.num; j++){
				if(a[i] == res.a[j]){ flag = 1; res.b[j] += b[i]; break;}
			}
			if(flag) continue;
			if(res.num < p){ res.a[res.num] = a[i]; res.b[res.num] = b[i]; ++res.num; continue;}
			int k = 0;
			for(int j = 1; j < res.num; j++) if(res.b[j] < res.b[k]) k = j;
			if(b[i] < res.b[k]){ for(int j = 0; j < res.num; j++) res.b[j] -= b[i];}
			else{
				int tmp = res.b[k];
				res.a[k] = a[i]; res.b[k] = b[i];
				for(int j = 0; j < res.num; j++) res.b[j] -= tmp;
			}
		} return res;
	} 
} t[N<<2];
void build(int x, int l, int r){
	if(l == r){ t[x].a[0] = a[l]; t[x].b[0] = t[x].num = 1; return;}
	int mid = (l+r) >> 1; build(x<<1, l, mid); build(x<<1|1, mid+1, r);
	t[x] = t[x<<1] + t[x<<1|1];
}
void pushnow(int x, int l, int r, int v){
	t[x].num = 1; t[x].a[0] = v; t[x].b[0] = r - l + 1; t[x].tag = v;
}
void pushdown(int x, int l, int r){
	if(t[x].tag){ 
		int mid = (l+r) >> 1;
		pushnow(x<<1, l, mid, t[x].tag);
		pushnow(x<<1|1, mid+1, r, t[x].tag);
		t[x].tag = 0;
	}
}
void modify(int x, int l, int r, int L, int R, int v){
	if(L<=l && r<=R){ pushnow(x, l, r, v); return;} 
	pushdown(x, l, r); int mid = (l+r) >> 1;
	if(L<=mid) modify(x<<1, l, mid, L, R, v);
	if(R>mid) modify(x<<1|1, mid+1, r, L, R, v);
	t[x] = t[x<<1] + t[x<<1|1];
}
tree query(int x, int l, int r, int L, int R){
	if(L<=l && r<=R) return t[x]; pushdown(x, l, r); 
	int mid = (l+r) >> 1;
	if(L>mid) return query(x<<1|1, mid+1, r, L, R);
	else if(R<=mid) return query(x<<1, l, mid, L, R);
	else return query(x<<1, l, mid, L, R) + query(x<<1|1, mid+1, r, L, R);
}
int main(){
	n = read(), m = read(), p = read(); p = 100 / p;
	for(int i = 1; i <= n; i++) a[i] = read();
	build(1, 1, n);
	while(m--){
		int op = read(), l = read(), r = read();
		if(op == 1) modify(1, 1, n, l, r, read()); 
		if(op == 2){
			tree ans = query(1, 1, n, l, r); 
			cout << ans.num << " ";
			for(int i = 0; i < ans.num; i++) cout << ans.a[i] << " ";
			puts("");
		}
	} return 0;
}

CF453E Little Pony and Lord Tirek
可以先算出每个马回满血的时间 t i t_i ti
然后对于区间 [ l , r ] [l,r] [l,r] 之间的马按 t i t_i ti 排序,二分出一个位置使得前面的都回满血,后面的都没有
处理出 s u m ( m i ) , s u m ( r i ) sum(m_i),sum(r_i) sum(mi),sum(ri) 就可以询问了,按 t i t_i ti为下标建主席树就刚刚好
看看还要支持什么操作:将 [ l , r ] [l,r] [l,r] 不同的 t t t的区间嗲出来然后全部赋成当前 t t t,用 O D T ODT ODT解决

#include<bits/stdc++.h>
#define N 100050
using namespace std;
typedef long long ll;
const int up = 100000;
int n, m, s[N], mx[N], rp[N];
int b[N], c[N]; ll sum[N];
#define pi pair<ll, ll>
#define mp make_pair
pi operator + (const pi &a, const pi &b){ return mp(a.first + b.first, a.second + b.second);}
struct Presidentree{
	bool op;
	int rt[N], ls[N * 20], rs[N * 20], node;
	ll val[N * 20], sum[N * 20];
	void modify(int &x, int las, int l, int r, int pos, int p){
		x = ++node; ls[x] = ls[las]; rs[x] = rs[las];
		val[x] = val[las] + rp[p]; sum[x] = sum[las] + mx[p] - (op ? 0 : s[p]);
		if(l == r) return; int mid = (l+r) >> 1;
		if(pos <= mid) modify(ls[x], ls[las], l, mid, pos, p);
		else modify(rs[x], rs[las], mid+1, r, pos, p);
	}
	pi query(int a, int b, int l, int r, int L, int R){
		if(!a) return mp(0, 0);
		if(L<=l && r<=R) return mp(val[a] - val[b], sum[a] - sum[b]);
		int mid = (l + r) >> 1; pi ans = mp(0, 0);
		if(L<=mid) ans = ans + query(ls[a], ls[b], l, mid, L, R);
		if(R>mid) ans = ans + query(rs[a], rs[b], mid+1, r, L, R);
		return ans;
	}
}T[2];
struct Node{ 
	int l, r, v; 
	Node(int _l = 0, int _r = 0, int _v = 0):l(_l),r(_r),v(_v){}
	bool operator < (const Node &a) const{ return l < a.l;}
};
set<Node> S;
typedef set<Node>::iterator Int;
Int split(int p){
	Int it = S.lower_bound(Node(p));
	if(it != S.end() && it->l == p) return it;
	it--;
	int l = it->l, r = it->r, v = it->v;
	S.erase(it);
	S.insert(Node(l, p - 1, v));
	it = S.lower_bound(Node(l, p - 1, v));
	return S.insert(Node(p, r, v)).first;
}
ll ask(int t, int x, int y){
	Int R = split(y + 1), L = split(x);
	ll ans = sum[y] - sum[x - 1];
	for(Int it = L; it != R; it++){
		pi res;
		if(it->v == 0) res = T[0].query(T[0].rt[it->r], T[0].rt[it->l - 1], 0, up + 1, min(up + 1, t - it->v + 1), up + 1);
		else res = T[1].query(T[1].rt[it->r], T[1].rt[it->l - 1], 0, up + 1, min(up + 1, t - it->v + 1), up + 1);
		ans += 1ll * res.first * (t - it->v) - res.second;
	} S.erase(L, R); S.insert(Node(x, y, t));
	return ans;
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		scanf("%d%d%d", &s[i], &mx[i], &rp[i]);
		sum[i] = sum[i-1] + (ll)mx[i];
		if(rp[i] == 0) b[i] = c[i] = up + 1;
		else{ 
			b[i] = (mx[i] - s[i]) / rp[i] + ((mx[i] - s[i]) % rp[i] > 0);
			c[i] = mx[i] / rp[i] + (mx[i] % rp[i] > 0);
		}
	} T[0].op = 0; T[1].op = 1;
	for(int i = 1; i <= n; i++) T[0].modify(T[0].rt[i], T[0].rt[i-1], 0, up + 1, b[i], i);
	for(int i = 1; i <= n; i++) T[1].modify(T[1].rt[i], T[1].rt[i-1], 0, up + 1, c[i], i);
	S.insert(Node(1, n, 0)); S.insert(Node(n + 1, n + 1, 0));
	scanf("%d", &m);
	while(m--){
		int t, l, r; scanf("%d%d%d", &t, &l, &r);
		cout << ask(t, l, r) << '\n';
	} return 0;
}

CF997E Good Subsegments
一个段是好的,当且仅当 m a x ( a i ) − m i n ( a i ) = r − l max(a_i)-min(a_i)=r-l max(ai)min(ai)=rl
我们把询问离线,按 r r r 排序,用线段树动态维护 m a x ( a i ) − m i n ( a i ) − ( r − l ) max(a_i)-min(a_i)-(r-l) max(ai)min(ai)(rl) 的最小值以及最小值个数
考虑新增一个 a i a_i ai 的贡献,对于每一个位置 r r r会+1
同时维护一个单调栈,对于 a i a_i ai 能作为 m a x max max 的区间我们统一加上 a i − m a x a_i-max aimax m i n min min 同理
于是到一个 r r r,我们在线段树中查询 [ l , r ] [l,r] [l,r] 中 0 的个数
等等,这里查的是强制选 r 的,显然不对,于是扫到一个 r 后打一个标记表示将当前 r 对答案的贡献下放

#include<bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long ll;
int n, m, a[N], mi[N], mx[N], top1, top2;
int Min[N<<2], sum[N<<2], ti[N<<2], tag[N<<2]; ll ans[N<<2], Ans[N];
struct qu{ int l, r, id;} q[N];
bool cmp(qu a, qu b){ return a.r < b.r;}
void pushmi(int x, int v){ Min[x] += v; tag[x] += v;}
void pushti(int x, int v){ ans[x] += 1ll * sum[x] * v; ti[x] += v;}
void pushdown(int x){
	if(tag[x]){ pushmi(x<<1, tag[x]); pushmi(x<<1|1, tag[x]); tag[x] = 0;}
	if(ti[x]){ 
		if(Min[x<<1] == Min[x]) pushti(x<<1, ti[x]);
		if(Min[x<<1|1] == Min[x]) pushti(x<<1|1, ti[x]);
		ti[x] = 0;
	}
}
void pushup(int x){
	Min[x] = min(Min[x<<1], Min[x<<1|1]); sum[x] = 0;
	if(Min[x] == Min[x<<1]) sum[x] += sum[x<<1];
	if(Min[x] == Min[x<<1|1]) sum[x] += sum[x<<1|1];
	ans[x] = ans[x<<1] + ans[x<<1|1];
} 
void build(int x, int l, int r){
	sum[x] = 1; Min[x] = l;
	if(l == r) return; int mid = (l+r) >> 1; 
	build(x<<1, l, mid); build(x<<1|1, mid+1, r);
}
void modify(int x, int l, int r, int L, int R, int v){
	if(L<=l && r<=R){ pushmi(x, v); return;} 
	pushdown(x); int mid = (l+r) >> 1;
	if(L<=mid) modify(x<<1, l, mid, L, R, v);
	if(R>mid) modify(x<<1|1, mid+1, r, L, R, v);
	pushup(x);
} 
ll query(int x, int l, int r, int L, int R){
	if(L<=l && r<=R) return ans[x]; pushdown(x);
	int mid = (l+r) >> 1; ll ret = 0; 
	if(L<=mid) ret += query(x<<1, l, mid, L, R);
	if(R>mid) ret += query(x<<1|1, mid+1, r, L, R);
	return ret;
}
int main(){
	scanf("%d", &n); 
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	build(1, 1, n);
	scanf("%d", &m);
	for(int i = 1; i <= m; i++) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
	sort(q+1, q+m+1, cmp);
	for(int i = 1, j = 1; i <= n; i++){
		while(top1 && a[i] < a[mi[top1]]){
			modify(1, 1, n, mi[top1-1] + 1, mi[top1], a[mi[top1]] - a[i]);
			top1--;
		} mi[++top1] = i;
		while(top2 && a[i] > a[mx[top2]]){
			modify(1, 1, n, mx[top2-1] + 1, mx[top2], a[i] - a[mx[top2]]);
			top2--;
		} mx[++top2] = i;
		pushmi(1, -1);
		pushti(1, 1);
		while(j <= m && q[j].r == i) Ans[q[j].id] = query(1, 1, n, q[j].l, q[j].r), ++j;
	}
	for(int i = 1; i <= m; i++) cout << Ans[i] << "\n";
	return 0;
}

CF1083D The Fair Nut’s getting crazy
考虑枚举区间 [ b , c ] [b,c] [b,c],记 l i , r i l_i,r_i li,ri为颜色 i 的前面后面出现位置,那么满足条件的就是
m a x ( l i ) < b , m i n ( r i ) > c max(l_i) < b,min(r_i)>c max(li)<b,min(ri)>c,即 a ∈ [ m a x ( l i ) , b ) , d ∈ [ c , m i n ( r i ) ) a\in[max(l_i),b),d\in[c,min(r_i)) a[max(li),b),d[c,min(ri))
好像有一点像上一道题,枚举 c,维护 ∑ b ( b − m x ) ( m i − c ) \sum_b(b-mx)(mi-c) b(bmx)(mic)
∑ b ( b − m x ) ( m i − c ) = ∑ b b ∗ m i − m x ∗ m i − b ∗ c + c ∗ m x \sum_b(b-mx)(mi-c)=\sum_b b*mi-mx*mi-b*c+c*mx b(bmx)(mic)=bbmimxmibc+cmx
发现对于每一个 i i i, 需要维护 i ∗ m i i*mi imi, m i mi mi, m x mx mx, m i ∗ m x mi*mx mimx
同样用单调栈维护一下 mi,mx 即可

#include<bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long ll;
const int Mod = 1e9+7;
int L[N], R[N], a[N], b[N], lst[N];
int mi[N], top1, mx[N], top2, n; ll ans;
void Add(ll &a, ll b){ a = a + b; if(a >= Mod) a -= Mod;}
ll add(ll a, ll b){ return (a + b) % Mod;}
ll mul(ll a, ll b){ return a * b % Mod; }
struct Node{
	ll mx, mi, mxi, pmi, mit, mxt;
	void merge(Node a, Node b){ 
		mx = add(a.mx, b.mx), mi = add(a.mi, b.mi);
		mxi = add(a.mxi, b.mxi); pmi = add(a.pmi, b.pmi);
	}
}t[N<<2];
void pushup(int x){t[x].merge(t[x<<1], t[x<<1|1]);}
ll f(ll x){ return (x * (x + 1) / 2) % Mod;}
void pushmi(int x, int l, int r, int v){
	Add(t[x].mit, v);
	Add(t[x].mi, mul(v, r-l+1));
	Add(t[x].mxi, mul(v, t[x].mx));
	Add(t[x].pmi, mul(v, add(f(r), Mod-f(l-1))));
}
void pushmx(int x, int l, int r, int v){
	Add(t[x].mxt, v);
	Add(t[x].mx, mul(v, r-l+1));
	Add(t[x].mxi, mul(v, t[x].mi));
}
void pushdown(int x, int l, int r){
	int mid = (l+r) >> 1;
	if(t[x].mit){ pushmi(x<<1, l, mid, t[x].mit); pushmi(x<<1|1, mid+1, r, t[x].mit); t[x].mit = 0;}
	if(t[x].mxt){ pushmx(x<<1, l, mid, t[x].mxt); pushmx(x<<1|1, mid+1, r, t[x].mxt); t[x].mxt = 0;} 
}
void modify(int x, int l, int r, int L, int R, int v, int op){
	if(L<=l && r<=R){ 
		if(!op) pushmi(x, l, r, v);
		else pushmx(x, l, r, v);
		return;
	} pushdown(x, l, r); int mid = (l+r) >> 1;
	if(L<=mid) modify(x<<1, l, mid, L, R, v, op);
	if(R>mid) modify(x<<1|1, mid+1, r, L, R, v, op);
	pushup(x);
}
Node query(int x, int l, int r, int L, int R){
	if(L<=l && r<=R) return t[x]; pushdown(x, l, r);
	int mid = (l+r) >> 1; Node ans = (Node){0, 0, 0, 0, 0, 0};
	if(L<=mid) ans.merge(ans, query(x<<1, l, mid, L, R));
	if(R>mid) ans.merge(ans, query(x<<1|1, mid+1, r, L, R));
	return ans;
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
	sort(b + 1, b + n + 1); int siz = unique(b+1, b+n+1) - (b+1);
	for(int i = 1; i <= n; i++) a[i] = lower_bound(b+1, b+siz+1, a[i]) - b;
	for(int i = 1; i <= n; i++){
		L[i] = lst[a[i]] + 1; lst[a[i]] = i;
	}
	for(int i = 1; i <= siz; i++) lst[i] = n + 1;
	for(int i = n; i >= 1; i--){
		R[i] = lst[a[i]] - 1; lst[a[i]] = i;
	}
	for(int i = 1, pos = 1; i <= n; i++){
		while(top1 && L[i] >= L[mx[top1]]){
			modify(1, 1, n, mx[top1-1]+1, mx[top1], Mod-L[mx[top1]], 1);
			top1--;
		} modify(1, 1, n, mx[top1] + 1, i, L[i], 1); mx[++top1] = i;
		while(top2 && R[i] <= R[mi[top2]]){
			modify(1, 1, n, mi[top2-1]+1, mi[top2], Mod-R[mi[top2]], 0);
			top2--;
		} modify(1, 1, n, mi[top2] + 1, i, R[i], 0); mi[++top2] = i;
		pos = max(pos, L[i]);
		Node res = query(1, 1, n, pos, i);
		ll tmp = Mod - mul(i, add(f(i), Mod - f(pos-1)));
		Add(tmp, Mod - res.mxi);
		Add(tmp, res.pmi);
		Add(tmp, mul(i, res.mx));
		Add(ans, tmp);
	} cout << ans; return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值