【NOI模拟赛】良(l)心(x)蓝(l)题(数据结构)

题目背景

一般遇到一道恶心数据结构题时,我们会说它是良心蓝题,简称lxl题

题面

给定 n , m , A n,m,A n,m,A ,维护由序列构成的序列 a 1 , . . . , a n a_1,...,a_n a1,...,an ,初始 a i a_i ai 都包含一个元素 A + 1 A+1 A+1

m m m 次操作,操作有两类:

  1. 修改操作:给定 l , r , x l,r,x l,r,x ,对 l ≤ i ≤ r l\leq i\leq r lir ,在序列 a i a_i ai 开头插入元素 x x x

  2. 查询操作:给定 l , r l,r l,r ,查询 ∑ i = l r F ( a i , A ) \sum_{i=l}^{r} F(a_i,A) i=lrF(ai,A)

其中 F ( a i , A ) F(a_i,A) F(ai,A) 等于 a i a_i ai 最短的前缀长度,满足该前缀所有元素的积大于 A A A

输入格式

第一行三个整数 n , m , A n,m,A n,m,A

接下来 m m m 行,每行 1 , l , r , x 1,l,r,x 1,l,r,x 表示一个修改操作, 2 , l , r 2,l,r 2,l,r 表示一个查询操作 。

输出格式

对于每个查询操作输出一行答案。

样例

5 20 10
1 4 4 166348285
2 2 5
2 1 5
1 1 2 10
1 4 4 3
1 4 5 6
2 5 5
1 5 5 1
1 2 3 1
2 5 5
2 5 5
2 3 4
2 3 3
2 4 5
2 4 4
1 2 5 5
1 5 5 9
1 1 4 5
2 5 5
2 1 4
4
5
2
3
3
4
2
5
2
2
8

数据范围

1 ≤ n , m ≤ 5 × 1 0 5 , 1 ≤ A , x ≤ 1 0 9 1\leq n,m\leq 5\times 10^5,1\leq A,x\leq 10^9 1n,m5×105,1A,x109

5 s   ,   512 m b \rm5s~,~512mb 5s , 512mb

题解

使用差分,可以将区间查询分为两次前缀查询,然后我们就可以在大序列上做扫描线。

把询问和修改都离线下来。我们维护当前位置的时间轴,一次修改拆成两次后缀修改。

用线段树维护每一时刻对应的答案,维护时间区间内的修改操作个数和 x x x 的积(方便线段树上二分求出单点答案)。再用set维护 x > 1 x>1 x>1 的操作位置。

每当我们加入或删除一个修改操作时,会对时间轴上后面 log ⁡ A \log A logA x > 1 x>1 x>1 的位置以及这些位置之间的时间产生影响。假设这些时间点分别是 t 1 , t 2 , . . . , t k t_1,t_2,...,t_k t1,t2,...,tk ,由于答案的性质, [ t 1 , t 2 ) , . . . [ t k , m ) [t_1,t_2),...[t_k,m) [t1,t2),...[tk,m) 这些区间内的答案变化量都是一致的,可以直接区间修改。

但是我们需要的是前缀所有位置的答案和,所以我们可以再差分一下。每当对一个区间答案增加 x x x 时,假设当前扫描到大序列 i i i 处,就直接区间增加 x × ( n − i + 1 ) x\times (n-i+1) x×(ni+1) ,对后面的所有 a i a_i ai 都统计贡献。这样一来,满足我们扫到 i i i 时, a i , a i + 1 , . . . , a n a_i,a_{i+1},...,a_n ai,ai+1,...,an 各个时间点的答案都是一样的。我们只需要将该时间点的全局贡献减去 ( a i 在该时间点的贡献 ) × ( n − i ) (a_i 在该时间点的贡献)×(n-i) (ai在该时间点的贡献)×(ni) ,就可以得到该前缀在某时间的贡献。

时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n) ,需要精细卡常,智慧卡常,用高效的数据结构,比如zkw线段树。

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 500005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
#define UIN unsigned int
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
#define getchar() xchar()
inline LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
inline void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
inline void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
inline int mult(int a,int b) {LL x=a*1ll*b;return x>=0x3f3f3f3f ? 0x3f3f3f3f:x;}
int A,cnq,qx[MAXN];
LL as[MAXN];
vector<int> cg[MAXN];
vector<PR> bu[MAXN];
int sm[MAXN<<2],tre[MAXN<<2],M;
LL ksks[MAXN<<2];
void maketree(int n) {
	M=1;while(M<n+2)M<<=1;
	for(int i = (M<<1)-1;i > 0;i --) tre[i] = 1;
	for(int i = M;i > 0;i >>= 1) tre[i] = A+1;
}
inline void addpo(int x,int y,int c) {
	int s=M+x;tre[s]=y;sm[s]=c;s>>=1;
	while(s)tre[s]=mult(tre[s<<1],tre[s<<1|1]),sm[s]=sm[s<<1]+sm[s<<1|1],s>>=1;
}
inline void addans(int l,int r,LL y) {
	for(int s=M+l-1,t=M+r+1;(s>>1)^(t>>1);s>>=1,t>>=1) {
		if(!(s&1)) ksks[s^1] += y;
		if(t & 1) ksks[t^1] += y;
	} return ;
}
inline LL findp(int x) {
	int s=M+x;LL as=0;
	while(s) as+=ksks[s],s>>=1;
	return as;
}
inline int findlftas(int x) {
	int mt = 1,ct = 0;
	for(int t=M+x+1;t>0;t>>=1) {
		if(t & 1) {
			int nx = mult(mt,tre[t^1]);
			if(nx <= A) mt = nx,ct += sm[t^1];
			else {
				int s = t^1;
				while(s<M) {
					nx = mult(mt,tre[s<<1|1]);
					if(nx <= A) mt = nx,ct += sm[s<<1|1],s<<=1;
					else s = s<<1|1;
				}
				break;
			}
		}
	} return ct;
}
set<int> st;
int lp[MAXN],rp[MAXN],pre[MAXN];
void addq(int x,int op,int fr) {
	static int ar[105],tp;
	static int ps[105];
	ar[tp = 1] = x;
	int tl = *st.upper_bound(x),mt = 1;
	while(tl <= m && mt <= A) ar[++ tp] = tl,mt = mult(mt,qx[tl]),tl = rp[tl];
	for(int i = 1;i <= tp;i ++) {
		ps[i] = (pre[ar[i]] == -1||i == 1 ? (pre[ar[i]] = findlftas(ar[i])):pre[ar[i]]);
	} ar[tp + 1] = m+1;
	if(op>0) {
		if(qx[x] > 1) {
			st.insert(x); auto tl = st.find(x),tr = tl;
			tl --; lp[x] = *tl; rp[*tl] = x;
			tr ++; lp[*tr] = x; rp[x] = *tr;
		}
		addpo(x,qx[x],1);
	}
	else {
		if(qx[x] > 1) {
			int s = lp[x],o = rp[x];
			rp[s] = o; lp[o] = s; st.erase(x);
		}
		addpo(x,1,0);
	}
	for(int i = 1;i <= tp;i ++) {
		int nw = findlftas(ar[i]);
		addans(ar[i],ar[i+1]-1,(nw-ps[i])*1ll*fr);
		pre[ar[i]] = nw;
	} return ;
}
int main() {
	n = read();m = read();A = read();
	qx[0] = A+1; qx[m+1] = A+1;
	st.insert(0); st.insert(m+1); rp[0] = m+1; lp[m+1] = 0;
	maketree(m);
	for(int i = 1;i <= m;i ++) {
		pre[i] = -1;
		k = read();
		if(k == 1) {
			s = read(); o = read(); qx[i] = read();
			cg[s].push_back(i); cg[o+1].push_back(-i);
		}
		else {
			s = read();o = read();
			cnq ++;
			bu[s-1].push_back({i,-cnq});
			bu[o].push_back({i,cnq});
			as[cnq] = o-s+1;
		}
	}
	for(int i = 1;i <= n;i ++) {
		for(int j:cg[i]) {
			if(j>0) addq(j,1,n-i+1);
			else addq(-j,-1,n-i+1);
		}
		for(auto &j:bu[i]) {
			int x = j.FI;
			LL nm = findp(x) - findlftas(x)*1ll*(n-i);
			if(j.SE > 0) as[j.SE] += nm;
			else as[-j.SE] -= nm;
		}
	}
	for(int i = 1;i <= cnq;i ++) {
		AIput(as[i],'\n');
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值