(有任何问题欢迎留言或私聊
题意:
传送门:here
题目意思很裸,
t
(
4
)
组
,
n
(
50000
)
,
m
(
10000
)
t(4)组,n(50000),m(10000)
t(4)组,n(50000),m(10000),查询
[
L
,
R
]
[L,R]
[L,R]第
k
k
k小;单点修改权值。
方法非常多,什么分块,整体二分,树状数组套主席树,线段树/树状数组套平衡树,按值建线段树套平衡树等等。
放我学了的方法,其他的以后再学吧~(可能永远都不会学了)
分块:2020ms
整体二分:260ms
树状数组套主席树:140ms/520ms
分块:
询问是二分+分块查找的
k
k
k小;暴力修改一个块。具体看代码中的解释。
复杂度:询问:
O
(
l
o
g
(
n
+
m
)
×
n
)
O(log(n+m)\times \sqrt n)
O(log(n+m)×n);修改:
O
(
n
)
O(\sqrt n)
O(n)
推荐:qsc学姐的分块教学
AC代码: 2020ms
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#define mm1(a) memset((a),-1,sizeof((a)))
#define mm0(a) memset((a),0,sizeof((a)))
#define mmx(a) memset((a),0x3f,sizeof((a)))
#define fuck(x) cout<<"* "<<x<<"\n"
using namespace std;
typedef long long LL;
const int N = 50007;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int n,m;
int ar[N],l[N],r[N],belong[N];
int block,num;
vector<int>b[N];
void build(){//这是qsc学姐分块写法
block=sqrt(n);//块的大小
num=n/block;if(n%block)num++;//块的数量
for(int i=1;i<=num;++i){
l[i]=(i-1)*block+1;r[i]=i*block;//每一块的左右区间
}
r[num]=n;
for(int i=1;i<=n;++i){
belong[i]=(i-1)/block+1;//每一点属于哪一块
}
for(int i=0;i<=num;++i)b[i].clear();//一定要记得初始化
for(int i=1;i<=n;++i){
b[belong[i]].push_back(ar[i]);//把值放入块中
}
for(int i=1;i<=num;++i){
sort(b[i].begin(),b[i].end());//要排序,因为后面查询要用到lowerboud
}
}
int q(int m,int x,int y){//查询节点x到y中小于等于m的数的数量
int cnt=0;
if(belong[x]==belong[y]){//如果在同一块,直接暴力枚举
for(int i=x;i<=y;++i){
if(ar[i]<=m)cnt++;
}
return cnt;
}
for(int i=belong[x]+1;i<belong[y];++i){//两节点之间的完整的块使用lowerbound
cnt+=upper_bound(b[i].begin(),b[i].end(),m)-b[i].begin();
}
if(x==l[belong[x]]){//如果x是其所属块的左端点,也使用lowerbound
int i=belong[x];
cnt+=upper_bound(b[i].begin(),b[i].end(),m)-b[i].begin();
}else{
for(int i=x;i<=r[belong[x]];++i){
if(ar[i]<=m)cnt++;
}
}
if(y==r[belong[y]]){//如果y是其所属块的右端点
int i=belong[y];
cnt+=upper_bound(b[i].begin(),b[i].end(),m)-b[i].begin();
}else{
for(int i=l[belong[y]];i<=y;++i){
if(ar[i]<=m)cnt++;
}
}
return cnt;
}
int get(int k,int a,int B,int r){//二分查找第k小的数字,大部分人写二分的习惯都不同,尽量使用自己习惯的写法
int l=0,ans=0,mid;
while(l<=r){
mid=(l+r)>>1;
int cnt=q(mid,a,B);
if(cnt>=k){
ans=mid;
r=mid-1;
}else{
ans=mid+1;
l=mid+1;
}
}
return ans;
}
void ch(int &x,int &y){//交换值函数
x^=y^=x^=y;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int mmax=0;
for(int i=1;i<=n;++i){
scanf("%d",&ar[i]);
mmax=max(mmax,ar[i]);//mmax是二分查找的上界
}
build();
for(int i=0;i<m;++i){
char s[2];int a,B,c;
scanf("%s %d %d",s,&a,&B);
if(s[0]=='Q'){
scanf("%d",&c);
printf("%d\n",get(c,a,B,mmax) );
}else{
mmax=max(mmax,B);
int tmp=belong[a];//求出节点所在块
int pos=lower_bound(b[tmp].begin(),b[tmp].end(),ar[a])-b[tmp].begin();
ar[a]=b[tmp][pos]=B;//左右移动调整
while(pos>0&&b[tmp][pos]<b[tmp][pos-1]){
ch(b[tmp][pos],b[tmp][pos-1]); pos--;
}
int size=b[tmp].size()-1;
while(pos<size&&b[tmp][pos]>b[tmp][pos+1]){
ch(b[tmp][pos],b[tmp][pos+1]); pos++;
}
}
}
}
return 0;
}
不得不说,分块确实很暴力。
整体二分
就很板子的题。
整体二分类似于一些决策单调性的分治,可以解决诸多区间第
k
k
k小的问题。
s
o
l
v
e
(
l
,
r
,
L
,
R
)
solve(l,r,L,R)
solve(l,r,L,R)表示当前值域为
[
l
,
r
]
[l,r]
[l,r],当前操作为
[
L
,
R
]
[L,R]
[L,R]。
我们要对所有操作按照他们对应值域区间进行划分,并递归分治。分治层数只与值域区间相关,我们是带着和这个值域相关的询问向下分治。所以整体二分的复杂度也是很稳定的。
枚举 [ L , R ] [L,R] [L,R]:
- 如果当前操作是更新操作:若更新的值在 [ l , m i d ] [l,mid] [l,mid]内,则用树状数组在当前更新操作所代表的序列下标位置 + 1 +1 +1,并将次操作归纳到下一层左区间内;反之归纳到下一层的右区间内。
- 如果当前操作是查询操作,树状数组询问区间 [ c w [ i ] . l , c w [ i ] . r ] [cw[i].l,cw[i].r] [cw[i].l,cw[i].r]内的值域情况,如果不小于 c w [ i ] . k cw[i].k cw[i].k就归入下一层左区间内;反之减去相应大小并归纳到下一层的右区间内。
- 终止条件:值域只有一个数,更新相应答案即可。
AC_Code:260ms
const int INF = 0x3f3f3f3f;
const int MXN = 2e5 + 7;
const int MXE = 1e6 + 7;
int n, m;
int bit[MXN];
void bit_add(int x,int c){for(;x <= n+1;x += lowbit(x)) bit[x]+=c;}
int bit_query(int x){int ans = 0;for(;x;x -= lowbit(x))ans+=bit[x];return ans;}
int ar[MXN];
struct lp {
int l, r, v, id, ip;
}cw[MXN], cw1[MXN], cw2[MXN];
int ans[MXN];
int stk[MXN], top;
void solve(int l, int r, int L, int R) {
if(l > r || L > R) return;
if(l == r) {
for(int i = L; i <= R; ++i) if(cw[i].ip == 2) ans[cw[i].id] = l;
return;
}
int mid = (l + r) >> 1, cnt1 = 0, cnt2 = 0;
top = 0;
for(int i = L; i <= R; ++i) {
if(cw[i].ip == 1) {
if(cw[i].l <= mid) bit_add(cw[i].id, cw[i].v), cw1[++cnt1] = cw[i], stk[++top] = i;
else cw2[++cnt2] = cw[i];
}else {
int k = bit_query(cw[i].r) - bit_query(cw[i].l - 1);
if(k >= cw[i].v) cw1[++cnt1] = cw[i];
else cw[i].v -= k, cw2[++cnt2] = cw[i];
}
}
for(int i = 1; i <= top; ++i) bit_add(cw[stk[i]].id, -cw[stk[i]].v);
for(int i = 1; i <= cnt1; ++i) cw[L + i - 1] = cw1[i];
for(int i = 1; i <= cnt2; ++i) cw[L + cnt1 + i - 1] = cw2[i];
solve(l, mid, L, L + cnt1 - 1);
solve(mid + 1, r, L + cnt1, R);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
// freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
int tim = read();
while(tim --) {
n = read(), m = read();
int tot = 0, p = 0;
for(int i = 1; i <= n; ++i) ar[i] = read(), cw[++ tot] = {ar[i], 0, 1, i, 1};
char s[2];
int l, r, k;
for(int i = 1; i <= m; ++i) {
scanf("%s", s);
if (s[0] == 'Q') {
l = read(), r = read(), k = read();
cw[++tot] = {l, r, k, ++ p, 2};
}else {
l = read(), r = read();
cw[++tot] = {ar[l], 0, -1, l, 1};
cw[++tot] = {ar[l] = r, 0, 1, l, 1};
}
}
solve(0, INF, 1, tot);
for(int i = 1; i <= p; ++i) printf("%d\n", ans[i]);
}
return 0;
}
树状数组套主席树:
写法1:520ms
询问是二分+树套树,
O
(
l
o
g
(
n
+
m
)
×
l
o
g
(
n
)
×
l
o
g
(
n
)
)
O(log(n+m)\times log(n)\times log(n))
O(log(n+m)×log(n)×log(n))
更新是树状数组+主席树,
O
(
l
o
g
(
n
)
×
l
o
g
(
n
)
)
O(log(n)\times log(n))
O(log(n)×log(n))
写法2:140ms
更新同上,
O
(
l
o
g
(
n
)
×
l
o
g
(
n
)
)
O(log(n)\times log(n))
O(log(n)×log(n))
询问:递归分治,
O
(
l
o
g
(
n
)
×
l
o
g
(
n
)
)
O(log(n)\times log(n))
O(log(n)×log(n))
把初始需要询问的根节点先存下来,然后
O
(
l
o
g
(
n
)
)
O(log(n))
O(log(n))求出左子树的点数:
若小于等于
k
k
k,则根节点变成他们的左子树;反之,变成右子树并让
k
k
k减去左子树的点数。
AC代码1: 520ms
#include<bits/stdc++.h>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
#define eb emplace_back
#define o2(x) (x)*(x)
#define all(x) (x).begin(), (x).end()
#define BASE_MAX 62
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
inline LL read(){
LL x=0;int f=0;char ch=getchar();
while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x=f?-x:x;
}
inline void write(LL x) {
if(x==0){putchar('0'),putchar('\n');return;}
if(x < 0) {putchar('-');x=-x;}
static char s[23];int l = 0;
while(x!=0)s[l++]=x%10+48,x/=10;
while(l)putchar(s[--l]);
putchar('\n');
}
int lowbit(int x) {return x&(-x);}
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000007, 1000000009};
const LL BASE[] = {1572872831, 1971536491};
const int MXN = 1e5 + 7;
const int MXE = 2e6 + 7;
int n, m;
int k;
struct lp {
int l, r, sum;
}cw[MXE];
int NODE, Root[MXN], yRoot[MXN];
int ar[MXN], br[MXN];
struct node {
int opt, a, b, c;
}edg[MXN];
int get_id(int x) {
return lower_bound(br + 1, br + k, x) - br;
}
void yupdate(int &cur, int old, int l, int r, int p) {
cur = ++ NODE;
cw[cur] = cw[old];
++ cw[cur].sum;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) yupdate(cw[cur].l, cw[old].l, l, mid, p);
else yupdate(cw[cur].r, cw[old].r, mid+1, r, p);
}
void update(int &cur, int p, int v, int l, int r) {
if(!cur) cur = ++ NODE;
cw[cur].sum += v;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) update(cw[cur].l, p, v, l, mid);
else update(cw[cur].r, p, v, mid+1, r);
}
int query(int cur, int p, int l, int r) {
if(l == r) return cw[cur].sum;
int mid = (l + r) >> 1;
if(p <= mid) return query(cw[cur].l, p, l, mid);
else return cw[cw[cur].l].sum + query(cw[cur].r, p, mid+1, r);
}
int Find(int aim, int l, int r) {
int yuan = query(yRoot[r], aim, 1, k) - query(yRoot[l], aim, 1, k);
for(; r; r -= lowbit(r)) yuan += query(Root[r], aim, 1, k);
for(; l; l -= lowbit(l)) yuan -= query(Root[l], aim, 1, k);
return yuan;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("E://ADpan//in.in", "r", stdin);
//freopen("E://ADpan//out.out", "w", stdout);
#endif
int tim; scanf("%d", &tim);
while(tim --) {
for(int i = 0; i <= NODE; ++i) cw[i] = {0, 0, 0};
NODE = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]), br[i] = ar[i];
k = n;
for(int i = 1; i <= m; ++i) {
char s[2];
scanf("%s", s);
if(s[0] == 'Q') {
edg[i].opt = 1;
scanf("%d%d%d", &edg[i].a, &edg[i].b, &edg[i].c);
}else {
edg[i].opt = 2;
scanf("%d%d", &edg[i].a, &edg[i].b);
br[++k] = edg[i].b;
}
}
sort(br + 1, br + 1 + k);
k = unique(br + 1, br + 1 + k) - br;
for(int i = 1; i <= n; ++i) yupdate(yRoot[i], yRoot[i-1], 1, k, get_id(ar[i]));
for(int i = 1; i <= m; ++i) {
int a, b, c, d;
if(edg[i].opt == 1) {
a = edg[i].a, b = edg[i].b, c = edg[i].c;
int L = 1, R = k - 1, mid, ans = 1;
while(L <= R) {
mid = (L + R) >> 1;
if(Find(mid, a - 1, b) >= c) ans = mid, R = mid - 1;
else L = mid + 1;
}
printf("%d\n", br[ans]);
}else {
a = edg[i].a, b = edg[i].b;
c = get_id(ar[a]);
d = get_id(b);
ar[a] = b;
while(a <= n) {
update(Root[a], c, -1, 1, k);
update(Root[a], d, 1, 1, k);
a += lowbit(a);
}
}
}
for(int i = 1; i <= n; ++i) Root[i] = yRoot[i] = 0;
}
return 0;
}
AC代码2: 140ms
#include<bits/stdc++.h>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
#define eb emplace_back
#define o2(x) (x)*(x)
#define all(x) (x).begin(), (x).end()
#define BASE_MAX 62
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
inline LL read(){
LL x=0;int f=0;char ch=getchar();
while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x=f?-x:x;
}
inline void write(LL x) {
if(x==0){putchar('0'),putchar('\n');return;}
if(x < 0) {putchar('-');x=-x;}
static char s[23];int l = 0;
while(x!=0)s[l++]=x%10+48,x/=10;
while(l)putchar(s[--l]);
putchar('\n');
}
int lowbit(int x) {return x&(-x);}
const int INF = 0x3f3f3f3f;
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000007, 1000000009};
const LL BASE[] = {1572872831, 1971536491};
const int MXN = 1e5 + 7;
const int MXE = 1e6 + 7 + 5e5;
int n, m;
int k;
struct lp {
int l, r, sum;
}cw[MXE];
int NODE, Root[MXN], yRoot[MXN];
int ar[MXN], br[MXN];
struct node {
int opt, a, b, c;
}edg[MXN];
int get_id(int x) {
return lower_bound(br + 1, br + k, x) - br;
}
void yupdate(int &cur, int old, int l, int r, int p) {
cur = ++ NODE;
cw[cur] = cw[old];
++ cw[cur].sum;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) yupdate(cw[cur].l, cw[old].l, l, mid, p);
else yupdate(cw[cur].r, cw[old].r, mid+1, r, p);
}
void update(int &cur, int p, int v, int l, int r) {
if(!cur) cur = ++ NODE;
cw[cur].sum += v;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) update(cw[cur].l, p, v, l, mid);
else update(cw[cur].r, p, v, mid+1, r);
}
int query(int cur, int p, int l, int r) {
if(l == r) return cw[cur].sum;
int mid = (l + r) >> 1;
if(p <= mid) return query(cw[cur].l, p, l, mid);
else return cw[cw[cur].l].sum + query(cw[cur].r, p, mid+1, r);
}
int ls[105], rs[105];
int k_query(int l, int r, int p) {
if(l == r) return l;
int mid = (l + r) >> 1, tmp = 0;
for(int i = 1; i <= rs[0]; ++i) tmp += cw[cw[rs[i]].l].sum;
for(int i = 1; i <= ls[0]; ++i) tmp -= cw[cw[ls[i]].l].sum;
if(p <= tmp) {
for(int i = 1; i <= rs[0]; ++i) rs[i] = cw[rs[i]].l;
for(int i = 1; i <= ls[0]; ++i) ls[i] = cw[ls[i]].l;
return k_query(l, mid, p);
}else {
for(int i = 1; i <= rs[0]; ++i) rs[i] = cw[rs[i]].r;
for(int i = 1; i <= ls[0]; ++i) ls[i] = cw[ls[i]].r;
return k_query(mid+1, r, p - tmp);
}
}
int main(){
int tim; scanf("%d", &tim);
while(tim --) {
for(int i = 0; i <= NODE; ++i) cw[i] = {0, 0, 0};
NODE = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]), br[i] = ar[i];
k = n;
for(int i = 1; i <= m; ++i) {
char s[2];
scanf("%s", s);
if(s[0] == 'Q') {
edg[i].opt = 1;
scanf("%d%d%d", &edg[i].a, &edg[i].b, &edg[i].c);
}else {
edg[i].opt = 2;
scanf("%d%d", &edg[i].a, &edg[i].b);
br[++k] = edg[i].b;
}
}
sort(br + 1, br + 1 + k);
k = unique(br + 1, br + 1 + k) - br;
for(int i = 1; i <= n; ++i) yupdate(yRoot[i], yRoot[i-1], 1, k, get_id(ar[i]));
for(int i = 1; i <= m; ++i) {
int a, b, c, d;
if(edg[i].opt == 1) {
a = edg[i].a, b = edg[i].b, c = edg[i].c;
ls[0] = rs[0] = 0;
ls[++ls[0]] = yRoot[a-1];
rs[++rs[0]] = yRoot[b];
for(int l = a-1; l; l -= lowbit(l)) ls[++ls[0]] = Root[l];
for(int r = b; r; r -= lowbit(r)) rs[++rs[0]] = Root[r];
printf("%d\n", br[k_query(1, k, c)]);
}else {
a = edg[i].a, b = edg[i].b;
c = get_id(ar[a]);
d = get_id(b);
ar[a] = b;
while(a <= n) {
update(Root[a], c, -1, 1, k);
update(Root[a], d, 1, 1, k);
a += lowbit(a);
}
}
}
for(int i = 1; i <= n; ++i) Root[i] = yRoot[i] = 0;
}
return 0;
}