一些没必要单独记录的题的题解
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;
}

浙公网安备 33010602011771号