1901: Zju2112 Dynamic Rankings 整体二分

树状数组套主席树做法
整体二分也是很优秀的,具体可以参考2013年许昊然的论文。
首先本题是可以离线做的,考虑主席树的做法,每次在一个区间中二分一个值 mid ,看 <=mid <script type="math/tex" id="MathJax-Element-1143"><=mid</script>的数是否为 k 个,如果超过k个,则答案一定在区间 [l,mid] 中,否则在区间 [mid+1,r] 中。
然后整体二分又是什么呢?我们还是先二分一个值 mid ,然后看在每一个区间中, <=mid <script type="math/tex" id="MathJax-Element-1149"><=mid</script>的值是否为 k 个,如果超过k个,则这个询问区间对应的答案区间一定在 [l,mid] 中,且之后区间内在 [mid+1,r] 中的数对它不会造成影响。而如果不足 k 个,则[l,mid]区间中的数对这个询问区间的贡献是一定的,以后的递归无需再次计算,只要记录下来就好,而且答案一定在 [mid+1,r] 中,所以就将当前的区间分为了两个无关的部分,递归解决就好了。
实测比树套树快,而且比较好写。
写完去装逼的时候yzy神犇说树套树多么好写啊,又被神犇嘲讽了。

#include<iostream>
#include<cstdio>
#define N 20005
#define inf 1000000000
#define lowbit(i) (i&(-i))
using namespace std;
int n,m,cnt,dfn;
int tree[N],a[N],ans[N],tmp[N<<1];
struct node {int x,y,k,id,cur,type;} q[N<<1],q1[N<<1],q2[N<<1];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void add(int x,int val)
{
    for (int i=x;i<=n;i+=lowbit(i)) tree[i]+=val;
}
inline int query(int x)
{
    int tmp=0;
    for (int i=x;i;i-=lowbit(i)) tmp+=tree[i];
    return tmp;
}
void solve(int t,int w,int l,int r)
{
    if (t>w) return;
    if (l==r)
    {
        for (int i=t;i<=w;i++)
            if (q[i].type==3) ans[q[i].id]=l;
        return;
    }
    int mid=l+r>>1;
    for (int i=t;i<=w;i++)
        if (q[i].type==1&&q[i].y<=mid) add(q[i].x,1);
        else if (q[i].type==2&&q[i].y<=mid) add(q[i].x,-1);
        else if (q[i].type==3) tmp[i]=query(q[i].y)-query(q[i].x-1);
    for (int i=t;i<=w;i++)
        if (q[i].type==1&&q[i].y<=mid) add(q[i].x,-1);
        else if (q[i].type==2&&q[i].y<=mid) add(q[i].x,1);
    int p1=0,p2=0;
    for (int i=t;i<=w;i++)
        if (q[i].type==3)
        {
            if (tmp[i]+q[i].cur>=q[i].k) q1[++p1]=q[i];
            else q[i].cur+=tmp[i],q2[++p2]=q[i];
        }
        else
        {
            if (q[i].y<=mid) q1[++p1]=q[i]; else q2[++p2]=q[i];
        }
    for (int i=1;i<=p1;i++) q[t+i-1]=q1[i];
    for (int i=1;i<=p2;i++) q[t+p1+i-1]=q2[i];
    solve(t,t+p1-1,l,mid); solve(t+p1,w,mid+1,r);
}
int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++)
        a[i]=read(),q[++cnt].x=i,q[cnt].y=a[i],q[cnt].type=1;
    for (int i=1;i<=m;i++)
    {
        char opt[5];
        scanf("%s",opt);
        if (opt[0]=='Q')
            q[++cnt].x=read(),q[cnt].y=read(),q[cnt].k=read(),q[cnt].type=3,q[cnt].id=++dfn;
        else
        {
            int x=read(),y=read();
            q[++cnt].x=x; q[cnt].y=a[x]; q[cnt].type=2;
            q[++cnt].x=x; q[cnt].y=y; q[cnt].type=1;
            a[x]=y;
        }
    }
    solve(1,cnt,0,inf);
    for (int i=1;i<=dfn;i++) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值