Wannafly挑战赛15 D 数字串 区间逆序对

题目描述 

一个只含数字的字符串,q次操作,每次操作将第i位数字改为x,每次操作后,统计长度在[l, r]之间且首数字大于尾数字的子串的个数。

输入描述:

第一行一个只含数字的字符串;
第二行3个整数q, l, r;
接下来q行,每行两个整数i, x。

输出描述:

输出q行,每行一个整数,表示长度在[l, r]之间且首数字大于尾数字的子串的个数。
示例1

输入

585605
2 2 4
1 6
4 2

输出

7
8

备注:

设字符串长度为n则:
1 <= n <= 100000;
1 <= q <= 100000; 1 <= l <= r <= n; 1 <= i <= n; 0 <= x
<= 9;

题解:

相当于求间隔距离在[l, r]之间的逆序对数。
因为所有数在[0,9]之间,所以可用 10 个树状数组维护数字每种的前缀和。
初始化求第一遍 ans 的时候,对于每个数考虑前面与该点的距离在[l,r]之间的所有点。

每次修改,减去当前位置的数对答案的影响,再加上更新后的数对答案的影响。

考虑前面和后面与该点的距离在[l,r]之间的所有点,更新 ans 即可。

时间复杂度:n*logn


树状数组

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
char str[maxn];
int l,r,len;
struct BIT
{
    int c[10][maxn];
    int lowbit(int x) {return x & -x;}
    void init() 
    {
        memset(c,0,sizeof(c));
    }
    void update(int x,int pos,int val)
    {
        for(int i=pos;i<maxn;i+=lowbit(i)) c[x][i] += val;
        return ;  
    }
    int query(int x,int pos) 
    {
        int ans = 0;
        for(int i=pos;i>0;i-=lowbit(i)) ans += c[x][i];
        return ans;
    }
}bit;

int ask_left(int x,int pos)             /// 统计在[pos-r+1,pos-l+1] 中有多少大于x的 
{
    int ans = 0;
    for(int i=x+1;i<10;i++) ans += bit.query(i,max(pos-l+1,0)) - bit.query(i,max(pos-r,0));
    return ans;
}
int ask_right(int x,int pos,int len)    /// 统计在[pos+l-1,pos+r-1] 中有多少个小于x的
{
    int ans = 0;
    for(int i=x-1;~i;i--) ans += bit.query(i,min(pos+r-1,len)) - bit.query(i,min(pos+l-2,len));
    return ans;
}
int main()
{
    while(~scanf("%s",str+1))
    {
        bit.init();
        int q;
        scanf("%d%d%d",&q,&l,&r);
        int len = strlen(str+1);
        ll ans = 0;
        for(int i=1;i<=len;i++) {  /// 得出未修改前的总逆序对个数
            int x = str[i]-'0';
            bit.update(x,i,1);
            ans += ask_left(x,i); 
        }
        // printf("%d\n",ans);
        int pos,x;
        while(q--)
        {
            scanf("%d%d",&pos,&x);
            int y = str[pos] - '0';
            ans -= ask_left(y,pos) + ask_right(y,pos,len);  /// 删除当前位置对答案的影响
            bit.update(y,pos,-1);
            str[pos] = x + '0';
            bit.update(x,pos,1);
            ans += ask_left(x,pos) + ask_right(x,pos,len);  /// 增加当前位置对答案的影响
            printf("%lld\n",ans);
        }
    }
    return 0;
}

线段树

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll arr[maxn];
char str[maxn];
int l,r,len;
struct SegTree
{
    ll sum[maxn<<2];
    void init()
    {
        memset(sum,0,sizeof(sum));
    }
    void push_up(int rt)
    {
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
    void build(ll arr[],int l,int r,int rt)
    {
        if(l == r) {
            sum[rt] = arr[l];
            return ;
        }
        int mid = (l + r) >> 1;
        build(arr,l,mid,rt<<1);
        build(arr,mid+1,r,rt<<1|1);
        push_up(rt);
    }
    void activate(int pos,ll val,int l,int r,int rt)
    {
        if(l == r) {
            sum[rt] = val;
            return;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) activate(pos,val,l,mid,rt<<1);
        else activate(pos,val,mid+1,r,rt<<1|1);
        push_up(rt);
    }
    void update(int pos,ll val,int l,int r,int rt)
    {
        if(l == r)
        {
            sum[rt] += val;
            return ;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) update(pos,val,l,mid,rt<<1);
        else update(pos,val,mid+1,r,rt<<1|1);
        push_up(rt);
    }
    ll query(int ql,int qr,int l,int r,int rt)
    {
        if(ql == l && qr == r) return sum[rt];
        int mid = (l + r) >> 1;
        if(qr <= mid) return query(ql,qr,l,mid,rt<<1);
        if(ql > mid) return query(ql,qr,mid+1,r,rt<<1|1);
        return query(ql,mid,l,mid,rt<<1) + query(mid+1,qr,mid+1,r,rt<<1|1);
    }
}seg[10];
ll ask_left(int num,int pos)
{
    ll res = 0,temp;
    for(int i=num+1;i<10;i++) {
        if(pos-l+1 <= 0) temp = 0;
        else {
            if(pos-r+1 <= 0) temp = seg[i].query(1,pos-l+1,1,len,1);
            else temp = seg[i].query(pos-r+1, pos-l+1,1,len,1);
        } 
        res += temp;
    }
    return res;
}
ll ask_right(int num,int pos)
{
    ll res = 0,temp;
    for(int i=num-1;i>=0;i--) {
        if(pos+l-1>len) temp = 0;
        else {
            if(pos+r-1 > len) temp = seg[i].query(pos+l-1,len,1,len,1);
            else temp = seg[i].query(pos+l-1,pos+r-1,1,len,1);
        }
        res += temp;
    }
    return res;
}
int main()
{
    while(~scanf("%s",str))
    {
        int q;
        scanf("%d%d%d",&q,&l,&r);
        for(int i=0;i<10;i++) seg[i].init();

        len = strlen(str);
        for(int i=1;i<=len;i++) arr[i] = str[i-1]^'0';
        
        ll ans = 0;
        for(int i=1;i<=len;i++) {
            seg[arr[i]].update(i,1,1,len,1);
            ans += ask_left(arr[i],i);
        }
        //printf("%lld\n",ans);

        int pos,x;
        while(q--)
        {
            scanf("%d%d",&pos,&x);
            int y = arr[pos];
            ans -= ask_left(y,pos) + ask_right(y,pos);
            arr[pos] = x;
            seg[y].update(pos,-1,1,len,1);
            seg[x].update(pos,1,1,len,1);
            ans += ask_left(x,pos) + ask_right(x,pos);
            printf("%lld\n",ans);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值