• 【洛谷P2617】Dynamic Rankings


    题目

    题目链接:https://www.luogu.com.cn/problem/P2617
    给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\),需要支持两种操作:

    • Q l r k 表示查询下标在区间 \([l,r]\) 中的第 \(k\) 小的数
    • C x y 表示将 \(a_x\) 改为 \(y\)

    思路

    经典动态区间第 \(k\) 大问题。我们在做静态区间第 \(k\) 大的问题时,利用类似前缀和的思想求出一个区间值域位于 \([l,r]\) 的数字有多少个。然后为了节省空间将权值线段树“合并”为主席树。
    在动态区间第 \(k\) 大的问题中,我们无法再用主席树的方法求前缀和,考虑用树状数组求前缀和,这样每次求 \(log n\) 棵线段树的和。
    时间复杂度 \(O(n\log^2 n)\),空间复杂度 \(O(n\log n)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=200010,LG=20;
    int n,m,tot,cnt1,cnt2,a[N],b[N],rt[N],q1[LG],q2[LG];
    char ch;
    
    struct Query
    {
    	int l,r,k,opt;
    }ask[N];
    
    struct SegTree
    {
    	int tot,lc[N*4*LG],rc[N*4*LG],size[N*4*LG];
    	
    	int update(int x,int l,int r,int k,int val)
    	{
    		if (!x) x=++tot;
    		if (l==k && r==k)
    		{
    			size[x]+=val;
    			return x;
    		}
    		int mid=(l+r)>>1;
    		if (k<=mid) lc[x]=update(lc[x],l,mid,k,val);
    			else rc[x]=update(rc[x],mid+1,r,k,val);
    		size[x]=size[lc[x]]+size[rc[x]];
    		return x;
    	}
    	
    	int query(int l,int r,int k)
    	{
    		if (l==r) return l;
    		int mid=(l+r)>>1,sum=0;
    		for (int i=1;i<=cnt1;i++) sum-=size[lc[q1[i]]];
    		for (int i=1;i<=cnt2;i++) sum+=size[lc[q2[i]]];
    		if (sum<k)
    		{
    			for (int i=1;i<=cnt1;i++) q1[i]=rc[q1[i]];
    			for (int i=1;i<=cnt2;i++) q2[i]=rc[q2[i]];
    			return query(mid+1,r,k-sum);
    		}
    		else
    		{
    			for (int i=1;i<=cnt1;i++) q1[i]=lc[q1[i]];
    			for (int i=1;i<=cnt2;i++) q2[i]=lc[q2[i]];
    			return query(l,mid,k);
    		}
    	}
    }seg;
    
    void update(int x,int pos,int val)
    {
    	for (int i=pos;i<=tot;i+=i&-i)
    		rt[i]=seg.update(rt[i],1,tot,x,val);
    }
    
    int query(int l,int r,int k)
    {
    	cnt1=cnt2=0;
    	for (int i=l-1;i;i-=i&-i) q1[++cnt1]=rt[i];
    	for (int i=r;i;i-=i&-i) q2[++cnt2]=rt[i];
    	return seg.query(1,tot,k);
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		b[++tot]=a[i];
    	}
    	for (int i=1;i<=m;i++)
    	{
    		while (ch=getchar())
    			if (ch=='C' || ch=='Q') break;
    		if (ch=='Q')
    		{
    			scanf("%d%d%d",&ask[i].l,&ask[i].r,&ask[i].k);
    			ask[i].opt=1;
    		}
    		else
    		{
    			scanf("%d%d",&ask[i].l,&ask[i].k);
    			b[++tot]=ask[i].k;
    			ask[i].opt=2;
    		}
    	}
    	sort(b+1,b+1+tot);
    	tot=unique(b+1,b+1+tot)-b-1;
    	for (int i=1;i<=n;i++)
    	{
    		a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
    		update(a[i],i,1);
    	}
    	for (int i=1;i<=m;i++)
    		if (ask[i].opt==2) ask[i].k=lower_bound(b+1,b+1+tot,ask[i].k)-b;
    	for (int i=1;i<=m;i++)
    	{
    		if (ask[i].opt==1)
    			printf("%d\n",b[query(ask[i].l,ask[i].r,ask[i].k)]);
    		else
    		{
    			update(a[ask[i].l],ask[i].l,-1); update(ask[i].k,ask[i].l,1);
    			a[ask[i].l]=ask[i].k;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    [C/C++开发] Clion利用Docker开发和调试PHP扩展
    [C/C++开发] Clion利用Docker开发和调试PHP内核
    [C/C++开发] Clion利用Docker开发和调试Linux C/C++程序
    [Docker] 使用ubuntu涉及时区问题
    Oracle 导入 SQL 文件
    转载
    微信小程序
    微信小程序-点击复制功能
    服务器端基础概念
    VSCode 同步设置插件
  • 原文地址:https://www.cnblogs.com/stoorz/p/13469088.html
Copyright © 2020-2023  润新知