ZOJ2112 带修改求区间第k小的n种方法

(有任何问题欢迎留言或私聊

题意:

 传送门: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值