# [XSY3434] [UOJ431] time map

Firstly, we notice a property: if we classify the adjacent points with the same weight as a connected block, then a leaf node will pass up at most O ( log ⁡ V ) O(\log V) O(logV) connected blocks (because the parent node must be a subset of the child node).

It is also noted that the points belonging to the same connected block go in the same direction.

Then, if we can dynamically maintain the weight of a point and ask if it will go to the left when starting from a point (then it will always go to the left in the connected block to which it belongs), we can dichotomy where it will go to the left (obviously, the weights of each point in this section are the same, that is, we only need to calculate the length of this section to know the contribution of this section to the answer), and then jump to the next connected block. This is done once O ( log ⁡ n log ⁡ V ) O(\log n\log V) O(lognlogV) (the time for a single point to query the weight of a point is not considered temporarily).

So now the problem becomes how to dynamically maintain the weights of points.

It turns out that the segment tree is false, and we can completely transform the operation into an ordinary segment tree. What we need to maintain are: interval operation or,and,xor and interval query and.

Theoretically, it can be realized: or0,or1,and0,and1,xor0,xor1 these six operations actually correspond to four operations: invariant, negate, set 1, set 0, and any two of these four operations are still one of these four operations after merging (meaning lazy flag can be merged).

When the numbers in an interval are subject to one of the four operations, we need to dynamically maintain the and of the numbers in the interval. Due to the inversion operation, we need to maintain the or of the interval at the same time to update the and.

If you merge one by one, the time complexity of a single merge and update is O ( log ⁡ V ) O(\log V) O(logV).

In fact, we can use the pressure + position operation to speed up: 00 means constant, 01 means inverse, 10 means set 0, 11 means set 1, we use + + +Represents a merger, so there are:
a b + 00 = a b a b + 01 = a b ⊕ 01 a b + 10 = 10 a b + 11 = 11 \begin{aligned} ab+00&=ab\\ ab+01&=ab\oplus 01\\ ab+10&=10\\ ab+11&=11 \end{aligned} ab+00ab+01ab+10ab+11​=ab=ab⊕01=10=11​
Thus, we can get a binary accelerated merging method: a b + c d = ( a ∣ c , ( b & ( c ‾ ) ) ⊕ d ) ab+cd=(a|c,(b\&(\overline{c}))\oplus d) ab+cd=(a ∣ C, (B & (c)) ⊕ d), where c ‾ \overline{c} c is reverse operation.

When updating, you can also get the method of binary accelerated merging: (let the original and and or be x,y and ab merging respectively): ( x & a ‾ & b ‾ ) ⊕ ( y ‾ & a ‾ & b ) ⊕ ( a & b ) (x\&\overline{a}\&\overline{b})\oplus(\overline{y}\&\overline{a}\&b)\oplus (a\&b) (x&a&b)⊕(y​&a&b)⊕(a&b).

According to the truth, the correspondence between the four binary numbers 00, 01, 10 and 11 and the four operations can be arbitrary, because binary acceleration is actually the process of accelerating the classification discussion through binary operation.

Then the complexity of modifying and querying on the segment tree is O ( log ⁡ n ) O(\log n) O(logn).

In fact, the dichotomy mentioned at the beginning can be carried out on the line segment tree: we are actually looking for the smallest p p p satisfied and ⁡ [ l , p ] = and ⁡ [ l , r ] \operatorname{and}[l,p]=\operatorname{and}[l,r] and[l,p]=and[l,r] (go straight to the left).

Total time complexity O ( n + q a log ⁡ n + q b log ⁡ n log ⁡ V ) O(n+q_a\log n+q_b\log n\log V) O(n+qa logn+qb lognlogV), where q a q_a qa = number of modifications, q b q_b qb ， is the number of inquiries.

#include<bits/stdc++.h>

#define N 1000010
#define fi first
#define se second
#define ll long long
#define lc(u) ch[u][0]
#define rc(u) ch[u][1]
#define INF 0x7fffffff
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)

using namespace std;

inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}

struct tag
{
int a,b;
tag(){};
tag(int aa,int bb){a=aa,b=bb;}
};

tag operator + (tag a,tag b)
{
return tag(a.a|b.a,((~b.a)&a.b)^b.b);
}

struct msg
{
int x,y;
msg(){};
msg(int xx,int yy){x=xx,y=yy;}
};

msg operator + (msg a,tag b)
{
return msg((a.x&(~b.a)&(~b.b))^((~a.y)&(~b.a)&b.b)^(b.a&b.b),(a.y&(~b.a)&(~b.b))^((~a.x)&(~b.a)&b.b)^(b.a&b.b));
}

msg operator + (msg a,msg b)
{
return msg(a.x&b.x,a.y|b.y);
}

int n,q;
int rt,node,ch[N<<1][2],L[N<<1],R[N<<1],d[N<<1];
bool dtag[N<<2];
tag lazy[N<<2];
msg sum[N<<2];

vector<pii>vl[N],vr[N];

void dfs(int &u,int l,int r,int dep)
{
u=++node;
L[u]=l,R[u]=r;
d[u]=dep;
if(l==r)
{
vr[r].push_back(mk(l,u));
vl[l].push_back(mk(r,u));
return;
}
int mid=read();
vr[r].push_back(mk(l,u));
dfs(lc(u),l,mid,dep+1),dfs(rc(u),mid+1,r,dep+1);
vl[l].push_back(mk(r,u));
}

void up(int k)
{
sum[k]=sum[k<<1]+sum[k<<1|1];
}

void build(int k,int l,int r)
{
if(l==r)
{
int val=read();
sum[k]=msg(val,val);
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
up(k);
}

void downn(int k,tag now)
{
sum[k]=sum[k]+now;
if(!dtag[k]) lazy[k]=now,dtag[k]=1;
else lazy[k]=lazy[k]+now;
}

void down(int k)
{
if(dtag[k])
{
downn(k<<1,lazy[k]);
downn(k<<1|1,lazy[k]);
dtag[k]=0;
}
}

void update(int k,int l,int r,int ql,int qr,tag now)
{
if(ql<=l&&r<=qr)
{
downn(k,now);
return;
}
down(k);
int mid=(l+r)>>1;
if(ql<=mid) update(k<<1,l,mid,ql,qr,now);
if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,now);
up(k);
}

int queryand(int k,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return sum[k].x;
down(k);
int mid=(l+r)>>1;
if(qr<=mid) return queryand(k<<1,l,mid,ql,qr);
if(ql>mid) return queryand(k<<1|1,mid+1,r,ql,qr);
return queryand(k<<1,l,mid,ql,qr)&queryand(k<<1|1,mid+1,r,ql,qr);
}

int queryl(int k,int l,int r,int ql,int qr,int &land,int cmp)
{
if(ql<=l&&((land&sum[k].x)>cmp))
{
land&=sum[k].x;
return -1;
}
if(l==r) return l;
down(k);
int mid=(l+r)>>1;
if(ql<=mid)
{
int tmp=queryl(k<<1,l,mid,ql,qr,land,cmp);
if(tmp!=-1) return tmp;
}
return queryl(k<<1|1,mid+1,r,ql,qr,land,cmp);
}

int queryr(int k,int l,int r,int ql,int qr,int &land,int cmp)
{
if(r<=qr&&((land&sum[k].x)>cmp))
{
land&=sum[k].x;
return -1;
}
if(l==r) return l;
down(k);
int mid=(l+r)>>1;
if(qr>mid)
{
int tmp=queryr(k<<1|1,mid+1,r,ql,qr,land,cmp);
if(tmp!=-1) return tmp;
}
return queryr(k<<1,l,mid,ql,qr,land,cmp);
}

int main()
{
n=read(),q=read();
build(1,1,n);
dfs(rt,1,n,1);
while(q--)
{
int opt=read(),l=read(),r=read();
if(opt<=3)
{
int val=read();
if(opt==1) update(1,1,n,l,r,tag(~val,0));
if(opt==2) update(1,1,n,l,r,tag(val,val));
if(opt==3) update(1,1,n,l,r,tag(0,val));
}
else
{
int Ll=l,Rr=r;
int u=vl[l][lower_bound(vl[l].begin(),vl[l].end(),mk(r,0))-vl[l].begin()].se;
assert(L[u]==Ll&&R[u]==Rr);
ll ans=0;
while(u)
{
l=L[u],r=R[u];
int val=queryand(1,1,n,l,r);
bool popc=__builtin_popcount(val)&1;
if(popc)
{
int fuck=INF;
int p=queryl(1,1,n,l,r,fuck,val);
int v=vl[l][lower_bound(vl[l].begin(),vl[l].end(),mk(p,0))-vl[l].begin()].se;
ans+=1ll*val*(d[v]-d[u]+1);
u=lc(v);
}
else
{
int fuck=INF;
int p=queryr(1,1,n,l,r,fuck,val);
int v=vr[r][upper_bound(vr[r].begin(),vr[r].end(),mk(p,node+1))-vr[r].begin()-1].se;
ans+=1ll*val*(d[v]-d[u]+1);
u=rc(v);
}
}
printf("%lld\n",ans);
}
}
return 0;
}


Tags: Python Algorithm

Posted on Wed, 13 Oct 2021 19:22:27 -0400 by mihomes