一些没必要单独记录的题的题解

1. CF689D

直接枚举显然不行。
我们考虑枚举左端点,再求有几个可能的右端点。
最终的右端点必然是连续的一段区间,考虑通过二分得到这个区间的左/右边界。
以下考虑左端点是 \(p\),右端点的左右边界分别是 \(l,r\)
对于左边界的二分,我们找到第一个 \(l\) 使得 \(\mathtt{max}(p,l)\ge\mathtt{min}(p,l)\)
对于右边界的二分,我们找到最后一个 \(r\) 使得 \(\mathtt{max}(p,l)\ge\mathtt{min}(p,l)\)
其中 \(\displaystyle\mathtt{max}(l,r)=\max_{i=l}^ra_i,\mathtt{min}(l,r)=\min_{i=l}^rb_i\)
\(\max,\min\) 的查询考虑暴力建出 \(\tt ST\) 表。
最后记得特判 不存在可能的 \(\bm{l,r}\) 以及 \(\bm{r=n}\) 的情况

#import<stdio.h>
#import<iostream>

const int N = 2e5 + 5,LG = 20;

int n,a[N],b[N],lg2[N],st1[N][LG],st2[N][LG];

void init(){
	for(int i = 1;i <= n;++i) st1[i][0] = a[i],st2[i][0] = b[i];
	for(int i = 2;i <= n;++i) lg2[i] = lg2[i >> 1] + 1;
	for(int j = 1;(1 << j) <= n;++j)
		for(int i = 1;i + (1 << j) - 1 <= n;++i)
			st1[i][j] = std::max(st1[i][j - 1],st1[i + (1 << j - 1)][j - 1]),
			st2[i][j] = std::min(st2[i][j - 1],st2[i + (1 << j - 1)][j - 1]);
}

auto qry(int l,int r){
	int i = lg2[r - l + 1];
	std::pair<int,int> res;
	res.first = std::max(st1[l][i],st1[r - (1 << i) + 1][i]);
	res.second = std::min(st2[l][i],st2[r - (1 << i) + 1][i]);
	return res;
}

int search1(int p){
	int L = p,R = n;
	while(R - L + 1 > 5){
		int q = L + R >> 1;
		auto[mx,mn] = qry(p,q);
		mx >= mn ? R = q : L = q + 1;
	}
	for(int q = L;q <= R;++q){
		auto[mx,mn] = qry(p,q);
		if(mx >= mn) return q;
	}
	return -1;
}

int search2(int p){
	auto[mx,mn] = qry(p,n);
	if(mx == mn) return n;
	int L = p,R = n;
	while(R - L + 1 > 5){
		int q = L + R >> 1;
		auto[mx,mn] = qry(p,q);
		mx > mn ? R = q : L = q + 1;
	}
	for(int q = L;q <= R;++q){
		auto[mx,mn] = qry(p,q);
		if(mx > mn) return q - 1;
	}
	return -1;
}

int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;++i) scanf("%d",a + i);
	for(int i = 1;i <= n;++i) scanf("%d",b + i);
	init();
	long long res = 0;
	for(int i = 1;i <= n;++i){
		int l = search1(i),r = search2(i);
		if(~l && ~r) res += r - l + 1;
	}
	printf("%lld\n",res);
	return 0;
}

2. CF785E

考虑在每次修改后,计算这次修改对答案的影响,而非重算答案。
设初始答案 \(S=0\)
对于修改 \(x,y\),那么对于每个 \(p\in(x,y)\),有

  • 如果 \(a_x\gt a_p\),那么 \(S\gets S-1\)
  • 如果 \(a_x\lt a_p\),那么 \(S\gets S+1\)
  • 如果 \(a_p\lt a_y\),那么 \(S\gets S+1\)
  • 如果 \(a_p\gt a_y\),那么 \(S\gets S-1\)

区间小于(或者大于)一个数的个数的询问,可以分块,块内维护排好序的 vector,然后二分。
总时间复杂度 \(\mathcal O(n\sqrt n\log n)\)

#import<stdio.h>
#import<vector>
#import<algorithm>
#import<numeric>

#define rep(i,a,b) for(int i = (a);i <= (b);++i)

const int N = 2e5 + 5,SQ = 514;

int n,m,st[SQ],ed[SQ],bl[N],a[N];
std::vector<int> s[SQ]; long long res = 0;

void init(){
	int B = 444,C = (n - 1) / B + 1;
	rep(i,1,C){
		st[i] = ed[i - 1] + 1;
		ed[i] = (i == C) ? n : i * B;
		rep(j,st[i],ed[i]) bl[j] = i,s[i].push_back(j);
	}
}

int qry1(int x,int l,int r){
	int S = 0;
	auto b0 = [&S,x](int l,int r){ rep(i,l,r) S += a[i] < x; };
	if(bl[l] == bl[r]) return b0(l,r),S;
	rep(i,bl[l] + 1,bl[r] - 1)
		S += std::ranges::lower_bound(s[i],x) - begin(s[i]);
	b0(l,ed[bl[l]]); b0(st[bl[r]],r); return S;
}

int qry2(int x,int l,int r){
	int S = 0;
	auto b0 = [&S,x](int l,int r){ rep(i,l,r) S += a[i] > x; };
	if(bl[l] == bl[r]) return b0(l,r),S;
	rep(i,bl[l] + 1,bl[r] - 1)
		S += end(s[i]) - std::ranges::upper_bound(s[i],x);
	b0(l,ed[bl[l]]); b0(st[bl[r]],r); return S;
}

void upd(int x,int y){
	s[bl[x]].assign(a + st[bl[x]],a + ed[bl[x]] + 1);
	s[bl[y]].assign(a + st[bl[y]],a + ed[bl[y]] + 1);
	std::ranges::sort(s[bl[x]]); std::ranges::sort(s[bl[y]]);
}

int main(){
	scanf("%d%d",&n,&m);
	std::iota(a + 1,a + n + 1,1);
	init();
	while(m--){
		int x,y;
		scanf("%d%d",&x,&y);
		if(x > y) std::swap(x,y);
		if(x == y){ printf("%lld\n",res); continue; }
		a[x] < a[y] ? ++res : --res;
		if(x + 1 == y){ printf("%lld\n",res); std::swap(a[x],a[y]); upd(x,y); continue; }
		int u = qry1(a[x],x + 1,y - 1);
		int v = qry1(a[y],x + 1,y - 1);
		int len = (y - 1) - (x + 1) + 1;
		res += len - u; res -= len - v;
		res -= u; res += v;
		printf("%lld\n",res);
		std::swap(a[x],a[y]); upd(x,y);
	}
	return 0;
}

3. CF121E

首先容易观察到幸运数其实只有 \(30\) 左右个,并且 \(a\) 的值域非常的小。
然后我们考虑暴力分块。
对于每个块维护 \(cnt\) 代表每种数的个数。
散块的修改和询问就暴力。
整块的修改打 \(\tt tag\)
整块的询问就对于块 \(i\),枚举每一个幸运数 \(x\)\(\displaystyle\sum cnt_{i,x-tag_i}\)
很好写。
\(\mathcal O(30n\sqrt n)\) 只是看起来有点大。这题不卡常所以实现很轻松。

const int N = 1e5 + 5,V = 1e4 + 5;
const int SQ = 370;

int n,m,a[N],c[SQ][V],tag[SQ];
int B,C,st[SQ],ed[SQ],bl[N];
bool chk[N]; int val[50],sz = 0;

void build(){
	B = sqrt(n),C = (n - 1) / B + 1;
	rep(i,1,C){
		st[i] = ed[i - 1] + 1;
		ed[i] = (i == C) ? n : i * B;
		rep(j,st[i],ed[i]) bl[j] = i,++c[i][a[j]];
	}
	rep(i,1,V - 1){
		int j = i; chk[i] = 1;
		while(j){ if(j % 10 != 4 && j % 10 != 7) chk[i] = 0; j /= 10; }
		if(chk[i]) val[++sz] = i;
	}
}

void upd(int l,int r,int x){
	auto sk_upd = [x](int l,int r){ rep(i,l,r) --c[bl[i]][a[i]],++c[bl[i]][a[i] += x]; };
	auto zk_upd = [x](int i){ tag[i] += x; };
	if(bl[l] == bl[r]) return sk_upd(l,r);
	sk_upd(l,ed[bl[l]]); sk_upd(st[bl[r]],r);
	rep(i,bl[l] + 1,bl[r] - 1) zk_upd(i);
}

int qry(int l,int r){
	int s = 0;
	auto sk_qry = [&s](int l,int r){ rep(i,l,r) s += chk[a[i] + tag[bl[i]]]; };
	auto zk_qry = [&s](int i){ rep(j,1,sz) if(val[j] > tag[i]) s += c[i][val[j] - tag[i]]; };
	if(bl[l] == bl[r]) return sk_qry(l,r),s;
	sk_qry(l,ed[bl[l]]); sk_qry(st[bl[r]],r);
	rep(i,bl[l] + 1,bl[r] - 1) zk_qry(i);
	return s;
}

int main(){
	read(n,m);
	rep(i,1,n) read(a[i]);
	build();
	rep(i,1,m){
		char c; int l,r;
		read(c,l,r);
		if(c == 'a') upd(l,r,read());
		else print(qry(l,r),'\n');
	}
	return 0;
}

CF474F

我们考虑什么样的数才能满足条件。

首先它得是这个区间所有数的因数,即 \(a_i\mid\gcd(a_l,\cdots,a_r)\)

但是我们知道两个数的 \(\gcd\) 一定小于等于这两个数,所以 \(\gcd(a_l,\cdots,a_r)\le\min(a_l,\cdots,a_r)\le a_i\)

所以 \(a_i=\gcd(a_l,\cdots,a_r)\)

然后就是区间一个数的出现次数。

可以值域 vector 存每个数的所有出现位置,然后在这个数的 vector 里面二分算(这个 \(\tt trick\) 来自 P5048)。

#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define range(_) begin(_),end(_)

const int N = 1e5 + 5;

int n,m,a[N],b[N],cnt,st[20][N],lg2[N];
unordered_map<int,int> M;
vector<int> P[N];

int main(){
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",a + i),st[0][i] = a[i];
	rep(i,1,n) if(!M[a[i]]) M[a[i]] = ++cnt;
	rep(i,1,n) P[M[a[i]]].push_back(i);
	rep(i,2,n) lg2[i] = lg2[i >> 1] + 1;
	rep(j,1,19) rep(i,1,n - (1 << j) + 1)
		st[j][i] = __gcd(st[j - 1][i],st[j - 1][i + (1 << j - 1)]);
	scanf("%d",&m);
	for(int l,r,i;m--;){
		scanf("%d%d",&l,&r),i = lg2[r - l + 1];
		int v = M[__gcd(st[i][l],st[i][r - (1 << i) + 1])];
		printf("%d\n",(r - l + 1) - (upper_bound(range(P[v]),r) - lower_bound(range(P[v]),l)));
	}
	return 0;
}
posted @ 2022-08-06 18:26  One_Zzz  阅读(38)  评论(0)    收藏  举报