UOJ #671. 【UNR #5】诡异操作

题面传送门
首先当除法中\(v=1\)可以直接舍去。否则每个数最多会被除\(logV\)次。所以暴力除就好了。
与操作可以线段树上每个节点维护这个区间内每一位有几个。然后懒标记下放即可。
时间复杂度\(O((nlogV+qlogn)logV)\),但是因为\(logV=128,logn=18\)过不去。
考虑如何优化。可以发现每次暴力除以后需要重新对每个数拆位,并且每次Update的时候每个int内一部分是空的,无法并行。
又可以发现我们每个线段树节点存储了一个\(logn\times logV\)的\(01\)矩阵,我们每次并行计算优化的是\(logn\)
所以可以把这个矩阵反过来,用\(logn\)个数,第\(i\)个数表示每一位二进制的个数在该二进制表示下有没有这一位。
这样每次暴力除的时候直接把除完的数放在第\(0\)个数即可,这一部分时间复杂度变为\(O(nlogV)\)。
并且Update的时候我们可以从低位往高位做,用异或算剩下位,用与和或算进位。这样这一部分时间复杂度变成\(O(qlog^2n)\)
总时间复杂度\(O(nlogV+qlog^2n)\),注意常数,卡常卡了一晚上。
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N 300000
#define M 500000
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (m*x+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound 
using namespace std;typedef __uint128_t u128;
int n,m,k,x,y,lg[N+5],op;u128 A[N+5],w,Ans;const u128 INF=-1;
I u128 read() {static char buf[100];scanf("%s", buf);u128 res=0;for(int i=0;buf[i];++i) res=res<<4|(buf[i]<='9'?buf[i]-'0':buf[i]-'a'+10);return res;}
I void print(u128 res) {if(res>=16)print(res/16);putchar(res%16>=10?'a'+res%16-10:'0'+res%16);}
namespace Tree{
	#define ls now<<1
	#define rs now<<1|1
	u128 Pa[N+5<<5],*H=Pa,*F[N+5<<2],Fl[N+5<<2];char S[N+5<<2],G[N+5<<2];I void Up(int now){u128 Ns=0;for(RI i=0;i<=S[now];i++) F[now][i]=F[ls][i]^F[rs][i]^Ns,Ns=(Ns&(F[ls][i]|F[rs][i]))|(F[ls][i]&F[rs][i]);G[now]=G[ls]&G[rs];}
	I void BD(int l=1,int r=n,int now=1){F[now]=H;S[now]=lg[r-l+1];H+=S[now]+2;Fl[now]=INF;if(l==r) {F[now][0]=A[l];G[now]=(!A[l]);return;}int m=l+r>>1;BD(l,m,ls);BD(m+1,r,rs);Up(now);}
	I void PF(int now,u128 w){for(RI i=0;i<=S[now];i++) F[now][i]&=w;Fl[now]&=w;}I void P(int now){Fl[now]^INF&&(PF(ls,Fl[now]),PF(rs,Fl[now]),Fl[now]=INF);}
	I void Div(int l=1,int r=n,int now=1){if(G[now])return;if(l==r) {F[now][0]/=w;G[now]=(!F[now][0]);return;}int m=l+r>>1;P(now);x<=m&&(Div(l,m,ls),0);y>m&&(Div(m+1,r,rs),0);Up(now);}
	I void And(int l=1,int r=n,int now=1){if(G[now])return;if(x<=l&&r<=y) return PF(now,w);P(now);int m=l+r>>1;x<=m&&(And(l,m,ls),0);y>m&&(And(m+1,r,rs),0);Up(now);}
	I void Qry(int x,int y,int l=1,int r=n,int now=1){if(G[now]) return;if(x<=l&&r<=y){for(RI i=0;i<=S[now];i++) Ans+=F[now][i]<<i;return;}int m=l+r>>1;P(now);x<=m&&(Qry(x,y,l,m,ls),0);y>m&&(Qry(x,y,m+1,r,rs),0);}
	#undef ls
	#undef rs
}
int main(){
	//freopen("1.in","r",stdin);freopen("1.out","w",stdout);
	RI i;scanf("%d%d",&n,&m);for(i=2;i<=n;i++) lg[i]=lg[i/2]+1;for(i=1;i<=n;i++) A[i]=read();Tree::BD();while(m--)scanf("%d%d%d",&op,&x,&y),op==1?(w=read(),w^1&&(Tree::Div(),0)):(op^3?(w=read(),Tree::And(),0):(Ans=0,Tree::Qry(x,y),print(Ans),Pc('\n')));
}
上一篇:使用随机16进制字符串和16进制时间戳生成token


下一篇:visual stdio使用spdlog教程