题目链接Problem - 1791F - Codeforces点击以获得更佳的阅读体验
F. Range Update Point Query
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Given an array a1,a2,…,an�1,�2,…,��, you need to handle a total of q� updates and queries of two types:
11 l� r� — for each index i� with l≤i≤r�≤�≤�, update the value of ai�� to the sum of the digits of ai��.
22 x� — output ax��.
Input
The first line of the input contains an integer t� (1≤t≤10001≤�≤1000) — the number of testcases.
The first line of each test case contains two integers n� and q� (1≤n,q≤2⋅1051≤�,�≤2⋅105) — the size of the array and the number of queries, respectively.
The second line of each test case contains n� integers a1,a2,…,an�1,�2,…,�� (1≤ai≤1091≤��≤109).
The next q� lines of each test case are of two forms:
11 l� r� (1≤l≤r≤n1≤�≤�≤�) — it means, for each index i� with l≤i≤r�≤�≤�, you should update the value of ai�� to the sum of its digits.
22 x� (1≤x≤n1≤�≤�) — it means you should output ax��.
There is at least one query of the second type.
The sum of n� over all test cases does not exceed 2⋅1052⋅105.
The sum of q� over all test cases does not exceed 2⋅1052⋅105.
Output
For each test case, output the answers of queries of the second type, in the order they are given.
Example
input
Copy
35 81 420 69 1434 20231 2 32 22 32 41 2 52 12 32 52 39999 10001 1 22 12 21 112 1
output
Copy
6
15
1434
1
6
7
36
1
1
Note
In the first test case, the following process occurs:
Initially, a=[1,420,69,1434,2023]�=[1,420,69,1434,2023].
The operation is performed for l=2�=2, r=3�=3, yielding [1,6,15,1434,2023][1,6,15,1434,2023].
We are queried for x=2�=2, x=3�=3, and x=4�=4, and output 66, 1515, and 14341434.
The operation is performed for l=2�=2, r=5�=5, yielding [1,6,6,12,7][1,6,6,12,7].
We are queried for x=1�=1, x=3�=3, and x=5�=5, and output 11, 66, and 77.
题意是有两个操作,
1操作是给出一个区间l到r,这个区间的每个数都变成它们各个数位上的树之和,如420就会变成6
2 操作是输出该下标的数的值。
树状数组做法
本题有两种做法,一种是用树状数组平推,这种做法基本不需要动脑子,只要记得树状数组的几种应用情况就行,唯一需要动脑子的地方就是要考虑清楚树状数组中存的是什么,以及区间修改时需要使用差分树状数组。
以下是树状数组单点修改的理解,理解这个其实基本上就理解了树状数组。
1(001) C[1]+=A[1]
lowbit(1)=001 1+lowbit(1)=2(010) C[2]+=A[1]
lowbit(2)=010 2+lowbit(2)=4(100) C[4]+=A[1]
lowbit(4)=100 4+lowbit(4)=8(1000) C[8]+=A[1]
————————————————
版权声明:本文为CSDN博主「bestsort」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bestsort/article/details/80796531
关于更多的树状数组可以看看上面的博客链接,讲的很好,如果对lowbit操作不清楚也可以看上面的博客。
code:
#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
#include<vector>
#include<cstring>
#include<map>
#include<set>
using namespace std;
const int manx = 2e5 + 10;
typedef long long ll;
int cnt[manx];
int lowbit(int x)
{
return x & -x;
}
int check(int x)
{
int k = 0;
while (x)
{
k += x % 10;
x = x / 10;
}
return k;
}
int a[manx];
void update(int c, int n, int k)
{
for (int i = c; i <= n; i+=lowbit(i))
{
cnt[i] += k;
}
}
int getsum(int c)
{
ll sum = 0;
for (int i = c; i; i -= lowbit(i))
{
sum += cnt[i];
}
return sum;
}//求前缀和是为了知道在该位置进行了几次操作1
void solve()
{
int n,q;
cin >> n>>q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cnt[i] = 0;
}
while (q--)
{
int x;
cin >> x;
if (x == 1)
{
int l, r;
cin >> l >> r;
update(l, n, 1);
update(r + 1, n, -1);//更新差分数组,这里差分的数组的目的是为了求前缀和时,每个下标的操作1的个数啊,这样就可以准确的算出在该位置进行了几次操作1
}
else
{
int c;
cin >> c;
ll ans = a[c];
for (int i = 1; i <= getsum(c); i++)
{
ans = check(ans);
if (ans < 10)break;
}
cout << ans << endl;
}
}
}
int main()
{
int t;
cin >> t;
//t = 1;
while (t--)
{
solve();
}
}
集合set的巧妙做法。
集合set做法是发现,当1操作的数小于10时,这个数的值就不会再变了,那么可以把所有大于10数的下标都放入集合set中,如果这个数操作后小于10了,那么就把这个数的下边从set里弹出。
#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
#include<vector>
#include<cstring>
#include<map>
#include<set>
using namespace std;
const int manx = 2e5 + 10;
typedef long long ll;
int cnt[manx];
int lowbit(int x)
{
return x & -x;
}
int check(int x)
{
int k = 0;
while (x)
{
k += x % 10;
x = x / 10;
}
return k;
}
int a[manx];
void update(int c, int n, int k)
{
for (int i = c; i <= n; i+=lowbit(i))
{
cnt[i] += k;
}
}
int getsum(int c)
{
ll sum = 0;
for (int i = c; i; i -= lowbit(i))
{
sum += cnt[i];
}
return sum;
}//求前缀和是为了知道在该位置进行了几次操作1
void solve()
{
int n,q;
cin >> n>>q;
set<int>s;
s.insert(n+1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] != check(a[i]))
{
s.insert(i);
}
}
while (q--)
{
int x;
cin >> x;
if (x == 1)
{
int l, r;
cin >> l >> r;
while (n)
{
auto pot = *s.lower_bound(l);
if (pot > r)
{
break;
}
a[pot] = check(a[pot]);
if (a[pot] == check(a[pot]))
{
s.erase(pot);
}
l = pot + 1;
}
}
else
{
int pot;
cin >> pot;
cout << a[pot] << endl;
}
}
}
int main()
{
int t;
cin >> t;
//t = 1;
while (t--)
{
solve();
}
}