题目描述
一个只含数字的字符串,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; }