Distorted Fate solution
D.Distorted Fate 题解
出题人题解。
Subtask 0(20 pts)
直接照着题目上的式子模拟就可以了,复杂度
#include<bits/stdc++.h>
using namespace std;
const int N=200005,mod=(1<<30);
int n,q,a[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,opt,l,r,x;i<=q;i++){
cin>>opt>>l>>r;
if(opt==1){
cin>>x;
for(int j=l;j<=r;j++) a[j]^=x;
}else{
long long ans=0;
for(int j=l;j<=r;j++){
int tmp=0;
for(int k=l;k<=j;k++) tmp|=a[k];
ans=(ans+tmp)%mod;
}
cout<<ans<<"\n";
}
}
}
Subtask 1(40 pts)
发现
#include<bits/stdc++.h>
using namespace std;
const int N=200005,mod=(1<<30);
int n,q,a[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,opt,l,r,x;i<=q;i++){
cin>>opt>>l>>r;
if(opt==1){
cin>>x;
for(int j=l;j<=r;j++) a[j]^=x;
}else{
long long ans=0,tmp=0;
for(int j=l;j<=r;j++){
tmp|=a[j];
ans=(ans+tmp)%mod;
}
cout<<ans<<"\n";
}
}
}
Subtask 2(60 pts)
既然我们可以使用前缀或统计答案,说明在求和的右端点
所以我们可以得到一个思路,找到答案改变的位置,这个可以很容易地使用线段树上二分做到。
考虑一个好写但时空复杂度不会变的思路,将每个数按二进制位拆开,开
从第一个
线段树节点记录区间内
对每一位询问一次,需要询问
同理,单次修改也要对应修改每一位,时间复杂度也是
所以总的时间复杂度就是
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,C=30,mod=1<<30;
int n,q,a[N];
struct segment_tree{
struct segment_tree_node{int num;bool tag;}t[N<<2];
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void f(int p,int l,int r){t[p].num=(r-l+1)-t[p].num,t[p].tag^=1;}
#define mid ((l+r)>>1)
inline void push_down(int p,int l,int r){
if(!t[p].tag) return;
f(ls(p),l,mid);
f(rs(p),mid+1,r);
t[p].tag=0;
}
inline void push_up(int x){t[x].num=t[ls(x)].num+t[rs(x)].num;}
void build(int p,int l,int r){
if(l==r) return t[p].num=a[l]&1,void();
else{
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
}
void change(int p,int l,int r,int re_l,int re_r){
if(re_l<=l&&r<=re_r) return f(p,l,r);
else{
push_down(p,l,r);
if(re_l<=mid) change(ls(p),l,mid,re_l,re_r);
if(mid<re_r) change(rs(p),mid+1,r,re_l,re_r);
push_up(p);
}
}
int find(int p,int l,int r,int k){
if(l==r) return l;
else{
push_down(p,l,r);
if(t[ls(p)].num>=k) return find(ls(p),l,mid,k);
else return find(rs(p),mid+1,r,k-t[ls(p)].num);
}
}
int query(int p,int l,int r,int re_l,int re_r){
if(re_l<=l&&r<=re_r) return t[p].num;
else if(!(r<re_l||l>re_r)) return push_down(p,l,r),query(ls(p),l,mid,re_l,re_r)+query(rs(p),mid+1,r,re_l,re_r);
else return 0;
}
}T[30];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<C;i++){
T[i].build(1,1,n+1);
for(int j=1;j<=n;j++) a[j]>>=1;
}
for(int i=1,opt,l,r,x;i<=q;i++){
cin>>opt>>l>>r;
if(opt==1){
cin>>x;
for(int j=0;j<C;j++){
if(x&(1<<j)) T[j].change(1,1,n+1,l,r);
}
}else{
long long ans=0;
for(int j=0;j<C;j++){
int num=T[j].query(1,1,n+1,1,l-1),pos=T[j].find(1,1,n+1,num+1);
ans=(ans+(1ll<<j)*(r-min(r+1,pos)+1ll))%mod;
}
cout<<ans<<"\n";
}
}
}
Subtask 3(100pts)
Subtask 2 的做法在时间上已经足够优秀了,但是在空间上却不能满足要求。
考虑如何优化。
发现对于一个区间,我们并不关心其中
我们可以在线段树节点中分别记录区间中这一位或起来的值
考虑异或对区间带来的影响。
- 当且仅当
x=y=1 ,区间内全是1 ,这时,区间异或会使x 和y 均变为0 。 - 当且仅当
x=y=0 ,区间内全是0 ,这时,区间异或会使x 和y 均变为1 。 - 否则,异或不会改变
x 和y 的取值。
区间内有
这时候,我们就可以顺利进行修改和查询操作了。
这样就可以在线段树上二分了……吗?
考虑一个问题:为什么一定要在线段树中记录
因为这个条件可以具有可差分性,所以可以用线段树上二分求解。
而我们维护的这个信息很明显没有可差分性,那应该怎么办呢?
考虑修改一下线段树上二分的步骤。
在线段树上查询区间
按从左往右的顺序遍历每个段,这个可以在线段树上先遍历左子树,后遍历右子树实现,若遍历到这个段时,若区间里没有
否则就在这个区间里线段树上二分找到答案,然后不再遍历后面的区间,因为这个区间内线段树节点维护的信息与询问区间以外的部分无关,所以这时就不需要维护的数据满足可差分性了。
考虑时间复杂度,线段树上查询将区间分为
于是将线段树节点内存储的一个整型变量优化成了两个布尔变量,可以顺利通过 Subtask 3。
时间复杂度仍然是
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,C=30,mod=1<<30;
int n,q,a[N];
struct segment_tree{
struct segment_tree_node{bool x,y,tag;}t[N<<2];
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void f(int p){
if(t[p].x==t[p].y) t[p].x^=1,t[p].y^=1;
t[p].tag^=1;
}
#define mid ((l+r)>>1)
inline void push_down(int p){
if(!t[p].tag) return;
f(ls(p)),f(rs(p));
t[p].tag=0;
}
inline void push_up(int p){
t[p].x=t[ls(p)].x|t[rs(p)].x;
t[p].y=t[ls(p)].y&t[rs(p)].y;
}
void build(int p,int l,int r){
if(l==r) return t[p].x=t[p].y=a[l]&1,void();
else{
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
}
void change(int p,int l,int r,int re_l,int re_r){
if(re_l<=l&&r<=re_r) return f(p);
else{
push_down(p);
if(re_l<=mid) change(ls(p),l,mid,re_l,re_r);
if(mid<re_r) change(rs(p),mid+1,r,re_l,re_r);
push_up(p);
}
}
int find(int p,int l,int r){
if(l==r) return l;
else{
push_down(p);
if(t[ls(p)].x) return find(ls(p),l,mid);
else return find(rs(p),mid+1,r);
}
}
int query(int p,int l,int r,int re_l,int re_r){
if(re_l<=l&&r<=re_r){
if(t[p].x) return find(p,l,r);
else return -1;
}else if(!(r<re_l||l>re_r)){
push_down(p);
int ansl=query(ls(p),l,mid,re_l,re_r);
if(ansl!=-1) return ansl;
else return query(rs(p),mid+1,r,re_l,re_r);
}else return -1;
}
}T[30];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<C;i++){
T[i].build(1,1,n+1);
for(int j=1;j<=n;j++) a[j]>>=1;
}
for(int i=1,opt,l,r,x;i<=q;i++){
cin>>opt>>l>>r;
if(opt==1){
cin>>x;
for(int j=0;j<C;j++){
if(x&(1<<j)) T[j].change(1,1,n+1,l,r);
}
}else{
long long ans=0;
for(int j=0;j<C;j++){
int pos=T[j].query(1,1,n+1,l,r);
if(pos!=-1) ans=(ans+(1ll<<j)*(r-min(r+1,pos)+1ll))%mod;
}
cout<<ans<<"\n";
}
}
}