Newcoder 16 C.Sum(線段樹)
阿新 • • 發佈:2018-11-04
Description
考慮維護一個這樣的問題:
(1) 給出一個數組 ,標號為 ~
(2) 修改陣列中的一個位置。
(3) 詢問區間 中所有子集的位運算 之和 。
我們定義集合
設 (設 為 集大小,若 則 )
所有子集的位運算 之和即為
那麼,現在問題來了。
Input
第一行,一個正整數
第二行, 個非負整數,為陣列
第三行,一個正整數 ,為操作次數
接下來 行格式如下
修改操作: ,將 修改為
詢問操作: ,區間 中所有子集的位運算 之和
Output
對於每次詢問輸出一行,為該次詢問的答案 。
請使用
Sample Input
3
1 2 3
6
2 1 3
1 1 2
2 1 3
2 2 3
1 2 5
2 1 3
Sample Output
9
15
7
13
Solution
按位考慮貢獻,若查詢區間的一個子集在第 位與後為 ,那麼該子集中每個元素在第 位均為 ,假設該區間有 個數字在第 位為 ,那麼第 位對答案的貢獻即為 ,用線段樹維護每個區間第 位為 的數字個數即可,時間複雜度
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=100005;
#define mod 1000000007
int add(int x,int y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/mod*mod;
}
#define ls (t<<1)
#define rs ((t<<1)|1)
int Num[maxn<<2][30],ans[30];
void push_up(int t)
{
for(int i=0;i<30;i++)Num[t][i]=Num[ls][i]+Num[rs][i];
}
void modify(int x,int l,int r,int t,int val)
{
if(l==r)
{
for(int i=0;i<30;i++)
if((val>>i)&1)Num[t][i]=1;
else Num[t][i]=0;
return ;
}
int mid=(l+r)/2;
if(x<=mid)modify(x,l,mid,ls,val);
else modify(x,mid+1,r,rs,val);
push_up(t);
}
void query(int L,int R,int l,int r,int t)
{
if(L<=l&&r<=R)
{
for(int i=0;i<30;i++)ans[i]+=Num[t][i];
return ;
}
int mid=(l+r)/2;
if(L<=mid)query(L,R,l,mid,ls);
if(R>mid)query(L,R,mid+1,r,rs);
return ;
}
int f[maxn];
int main()
{
f[0]=0;
for(int i=1;i<=1e5;i++)f[i]=add(add(f[i-1],f[i-1]),1);
int n,q;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
modify(i,1,n,1,x);
}
scanf("%d",&q);
while(q--)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==1)modify(l,1,n,1,r);
else
{
memset(ans,0,sizeof(ans));
query(l,r,1,n,1);
int res=0;
for(int i=0;i<30;i++)res=add(res,mul(f[ans[i]],1<<i));
printf("%d\n",res);
}
}
return 0;
}