题目大意:有一长度为n的数组a,有两种操作,有q次操作,操作1可以将a[l]和a[r]之间的数字变成a[i]的数位和,操作2求a[i]的值
1<=n,q<=2e5;1<=ai<=1e9
思路:因为对于1e9以内的数,数位和最大是9*9=81,最多变成三次数位和就会变成一位数,所以我们记录每个数进行操作1的次数,然后对于查询的数进行对应次数的修改即可,所以变成了区间修改+单点查询问题,用树状数组的时间复杂度常数比线段树低一些,本题用线段树会被卡TLE。所以我们用差分树状数组,这样的话查询tree[i]的值就等于求i及其之前的数的前缀和,区间修改也只要修改tree[l]++和tree[r-1]--即可
//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int a[N];
ll sum[N];
int n;
ll lowbit(ll x)
{
return x & (-x);
}
void update(int pos, int k)
{//单点修改
for (int i = pos; i <= n; i += lowbit(i))
sum[i] += k;
}
int cal(int x)
{//计算数位和
int ret = 0;
while (x)
{
ret += x % 10;
x /= 10;
}
return ret;
}
ll ask(int pos)
{//求前缀和
ll ans = 0;
for (int i = pos; i; i -= lowbit(i))
ans += sum[i];
return ans;
}
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
int q;
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = 0;//初始化树状数组
}
for (int i = 1; i <= q; i++)
{
int op;
cin >> op;
if (op == 2)
{
int it;
cin >> it;
int ans = a[it];//不能修改原数值
if (a[it] >= 10)
{//一位数直接输出即可
for (int i = 1; i <= ask(it); i++)
{
ans = cal(ans);
if (ans < 10)
break;//最多修改3次,变成一位数直接退出循环
}
}
cout << ans << endl;
}
else
{
int l, r;
cin >> l >> r;
update(l,1);
update(r + 1, -1);//对于差分树状数组的区间修改
}
}
}
return 0;
}