题面
题目大意就是给出一个区间[L,R],对于区间里的每个x,如果[1,n]中有一个y,使得y=(x << <script type="math/tex" id="MathJax-Element-28"><<</script>k)+c,其中c比(1 << <script type="math/tex" id="MathJax-Element-29"><<</script>k)小,那么a[y]+=d。给出a序列和每次修改的区间[L,R],每次问你a中某个数的值。
题解
作为某场GDKOI膜泥赛的T1,这道题是很水的。然而一开始我想了一些乱七八糟的东西。
对于修改直接考虑给区间[L,R]加权d,由于每次修改操作权可以叠加,所以搞个线段树。询问考虑一个数X能成为哪些数的约幂数。根据我玄学乱画,发现一个数X成为Y的约幂数,当且仅当Y是X二进制分解的前缀。于是每次logn分解X,在线段树上统计X被加了多少就行了。
下面简要证明一下Y是X的二进制前缀:
充分性:Y左移了k位,剩下的c一定在后面的0里出现,所以肯定小于最后那个1的权。
必要性:假如Y不是X的前缀,那么若X-Y得到的数所有的1都在X的最后一个1的右边,那么加起来必然不等于X,那么c必然有1出现在Y的最后一个1左边(或覆盖最后一个1),于是不满足条件。
于是本题愉快解决。
ps:听说也有直接枚举k的做法,不过下标会爆int什么的。。。
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#define maxn 100010
using namespace std;
typedef long long LL;
int n, m;
LL A[maxn];
struct Tnode{
LL sum, add;
}Tree[maxn<<2];
void Down(int root, int mid, int Lson, int Rson, int L, int R){
if(!Tree[root].add) return;
Tree[Lson].sum += Tree[root].add * (mid - L + 1);
Tree[Lson].add += Tree[root].add;
Tree[Rson].sum += Tree[root].add * (R - mid);
Tree[Rson].add += Tree[root].add;
Tree[root].add = 0LL;
}
void Update(int root, int L, int R, int x, int y, LL v){
if(x > R || y < L) return;
if(x <= L && y >= R){
Tree[root].sum += v * (R - L + 1);
Tree[root].add += v;
return;
}
int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
Down(root, mid, Lson, Rson, L, R);
Update(Lson, L, mid, x, y, v);
Update(Rson, mid+1, R, x, y, v);
Tree[root].sum = Tree[Lson].sum + Tree[Rson].sum;
}
LL Query(int root, int L, int R, int x){
if(x > R || x < L) return 0LL;
if(L == x && x == R) return Tree[root].sum;
int mid = (L + R) >>1, Lson = root << 1, Rson = root << 1 | 1;
Down(root, mid, Lson, Rson, L, R);
LL temp1 = Query(Lson, L, mid, x);
LL temp2 = Query(Rson, mid+1, R, x);
return temp1 + temp2;
}
int main(){
freopen("A.in", "r", stdin);
freopen("A.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%lld", &A[i]);
int op, l, r, x;
LL d;
for(int i = 1; i <= m; i++){
scanf("%d", &op);
if(op == 1){
scanf("%d%d%lld", &l, &r, &d);
Update(1, 1, n, l, r, d);
}
else{
scanf("%d", &x);
int temp = x;
LL res = 0LL;
while(temp){
res += Query(1, 1, n, temp);
temp >>= 1;
}
printf("%lld\n", A[x] + res);
}
}
return 0;
}
小舟从此逝 江海寄余生