[UNR#5]诡异操作

137 篇文章 1 订阅
11 篇文章 0 订阅

题目

传送门 to UOJ

思路

整除操作,可以考虑暴力。问题在于,我们要怎样维护可以快速 b i t a n d \rm bitand bitand 的结构,使得整除之后暴力 pushUp 的速度不算太慢?

很容易想到拆位,每一位记录一下,有多少个数在这一位上是 1 1 1 。那么做 b i t a n d \rm bitand bitand 需要 O ( log ⁡ V ) \mathcal O(\log V) O(logV),整除之后修改结果也是 O ( log ⁡ V ) \mathcal O(\log V) O(logV) 。整除过程中,每个节点势能为 O ( log ⁡ V ) \mathcal O(\log V) O(logV),所以分析出上界为 O ( n log ⁡ 2 V ) \mathcal O(n\log^2 V) O(nlog2V) 。而 log ⁡ V \log V logV 达到了惊人的 128 128 128,根本过不了。

这时候,我们想到一个神奇的 “分块”,那就是 b i t s e t \rm bitset bitset 。同时对 ω \omega ω 位进行操作,却是 O ( 1 ) \mathcal O(1) O(1) 的时间,这是一个 b u g \rm bug bug 般的存在!因为它真的是 让暴力变快,而不是减少冗余信息计算!

于是考虑将那 log ⁡ V \log V logV 个二进制位,用 b i t s e t \rm bitset bitset 实现 “变为零” 的操作。但是 b i t s e t \rm bitset bitset 又只针对 0 , 1 0,1 0,1 啊!所以我们对 值(因变量) 进行拆位。有一种比较形象的说法是,将第 x x x bit \text{bit} bit 的出现次数 c x c_x cx 用二进制写出来(写在一列上),那么原本我们是按照列存储,现在变为按照行存储,因为行数更少。

r x r_x rx 状压存储出现次数的 x -bit x\text{-bit} x-bit 1 1 1 的比特位。 b i t a n d \rm bitand bitand 操作就是对每个 r x r_x rx 进行按位与。时间复杂度 O ( log ⁡ L ) \mathcal O(\log L) O(logL),其中 L L L 为区间长度。而 pushUp 可以用大整数加法的方式实现(相当于并行,毕竟进位也是 0 , 1 0,1 0,1,也可以用 b i t s e t \rm bitset bitset 存储),也是 O ( log ⁡ L ) \mathcal O(\log L) O(logL)

复杂度是什么呢?老规矩,势能。一个被完全覆盖的节点要往下递归,最多 O ( log ⁡ V ) \mathcal O(\log V) O(logV) 次。整棵树的代价和 T ( n ) = 2 T ( n 2 ) + O ( log ⁡ n ) = O ( n ) T(n)=2T({n\over 2})+\mathcal O(\log n)=\mathcal O(n) T(n)=2T(2n)+O(logn)=O(n),所以势能带来的代价是 O ( n log ⁡ V ) \mathcal O(n\log V) O(nlogV),加上一般复杂度 O ( q log ⁡ 2 n ) \mathcal O(q\log^2 n) O(qlog2n) 可过。

代码

容易编译超时,建议 Clang 编译。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

typedef __uint128_t ulllong;
void readHex(ulllong &v){
	static int8_t haxi[1<<8];
	static bool done = false; if(!done){
		memset(haxi,-1,1<<8); // not good
		done = true; rep(i,0,9) haxi[i^48] = int8_t(i);
		rep(i,'a','f') haxi[i] = int8_t(i-'a'+10);
	}
	v = 0; int c = getchar(); // read by hex
	for(; ~haxi[c]; c=getchar()) v = (v<<4)^haxi[c];
}
void writeHex(const ulllong &v){
	if(v>>4) writeHex(v>>4);
	if((v&15) < 10) putchar(int((v&15)^48));
	else putchar(int((v&15)-10+'a'));
}

const int LOST = 1<<19; ///< length of segment tree
ulllong a[LOST|1]; ///< original array
template < int dep > ///< log(len)+1
struct Node{
	Node<dep-1> lson, rson;
	ulllong v[dep], sum, tag;
	Node():tag(~ulllong(0)){}
	void operator &= (const ulllong &w){
		tag &= w, sum = 0;
		for(int i=0; i!=dep; ++i)
			v[i] &= w, sum += v[i]<<i;
	}
	void pushDown(){
		lson &= tag, rson &= tag;
		tag = compl ulllong(0);
	}
	void pushUp(){
		ulllong &co = v[dep-1] = 0;
		for(int i=0; i!=dep-1; ++i){
			v[i] = lson.v[i] xor co xor rson.v[i];
			co = (lson.v[i]&(co|rson.v[i]))|(co&rson.v[i]);
		}
		sum = lson.sum+rson.sum;
	}
	void build(int l=1,int r=LOST){
		lson.build(l,(l+r)>>1), rson.build((l+r+2)>>1,r);
		pushUp(); // update sum and v
	}
	void div(int ql,int qr,const ulllong &qv,int l=1,int r=LOST){
		if(!sum || qr < l || r < ql) return ; // empty
		pushDown(), lson.div(ql,qr,qv,l,(l+r)>>1);
		rson.div(ql,qr,qv,(l+r+2)>>1,r), pushUp();
	}
	void band(int ql,int qr,const ulllong &qv,int l=1,int r=LOST){
		if(qr < l || r < ql) return ;
		if(ql <= l && r <= qr) return void(*this &= qv);
		pushDown(), lson.band(ql,qr,qv,l,(l+r)>>1);
		rson.band(ql,qr,qv,(l+r+2)>>1,r), pushUp();
	}
	ulllong query(int ql,int qr,int l=1,int r=LOST){
		if(qr < l || r < ql) return ulllong(0);
		if(ql <= l && r <= qr) return sum;
		pushDown(); return lson.query(ql,qr,l,(l+r)>>1)
			+ rson.query(ql,qr,(l+r+2)>>1,r);
	}
};
template < > // specialization
struct Node<1>{
	ulllong sum, *v;
	Node():sum(0),v(&sum){}
	void operator &= (const ulllong &w){
		sum &= w; // the same as v[0]
	}
	void div(int ql,int qr,const ulllong &qv,int l,int r){
		if(ql <= l && r <= qr) sum /= qv;
	}
	void band(int ql,int qr,const ulllong &qv,int l,int r){
		if(ql <= l && r <= qr) sum &= qv;
	}
	void build(int l,int r){ sum = a[l]; }
	ulllong query(int ql,int qr,int l,int r){
		return (ql <= l && r <= qr) ? sum : 0;
	}
};

Node<20> sgt;
int main(){
	int n = readint(), q = readint();
	rep(i,1,n) readHex(a[i]);
	sgt.build();
	for(int op,l,r; q; --q){
		op = readint(), l = readint(), r = readint();
		if(op == 3){
			writeHex(sgt.query(l,r));
			putchar('\n'); continue;
		}
		ulllong v; readHex(v);
		if(op == 1 && v != 1) sgt.div(l,r,v);
		else if(op == 2) sgt.band(l,r,v);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值