UniversalOJ 671 诡异操作 题解
这道题目出的还是非常的有含金量的,虽然对于要卡假做法时限卡的很紧表示理解,但是对于正解卡常也有点小严重我不做评价(或许这就是这道题目差评量\(\ge 70\)的原因吧。)
首先,这一道题目有一个显然的想法,我们可以建一颗线段树,维护一段区间中某一个二进制位出现的次数,操作\(2\)使用懒标记进行,操作\(1\)直接暴力推平,复杂度是\(O(q\log_2{n} \log_2{V})\),然后卡常死活看不过,直接放弃,不用思考了。
正确思路是考虑这么一个问题,我们维护一个区间中某个二进制位出现的次数,为什么会有一个\(\log_2{V}\),而不是\(\log_2{n}\)呢,容易发现是因为二进制位最多有\(\log_2{V}\)种,每一次合并都要\(O(\log_2{V})\)的复杂度,我们考虑设计一种合并只需要\(\log_2{n}\)的复杂度的状态,然后就是一个可能并不是很经典的trick了,我们不再维护一个区间中某个二进制位出现的次数,而去维护出现次数为一个二进制位的和,理解的话我们考虑列出以下表格。
记第\(i\)行表示\(2_i\),每一列依次表示出现了\(2_i\)次,比如第一行的实际含义就是\(2^0\)这一位总共出现了\(2^0+2^1+2^2=7\)次,我们考虑交换行和列进行统计,我们统计第一列,他表示出现了\(1\)次的有\(2^0+2^2=5\),因为这个矩阵中的每一位只有\(0\)或\(1\)。我们统计出来每一列的\(2_i\)的和也可以表示出来这个矩阵,而且我们容易发现我们现在合并的复杂度就是\(O(列数)\),也就是\(O(\log_2{n})\),在操作\(2\)的时候还可以之间使用与运算统计答案,操作\(2\)的复杂度就是\(O(q\log^2_2{n})\),这里有人可能会问:哎,但我操作\(1\)的复杂度还是\(O(q \log_2{n} \log_2{V})\)啊,我们来仔细分析一下这个东西的复杂度,根据线段树的性质,\(q\)次查询不满的区间只有\(O(q \log_2{n})\),每一次合并的复杂度是\(O(\log_2{n})\),总复杂度是\(O(q\log^2_2n)\),而查询满的区间显然最多查询\(\log_2{V}\)次(不然就全是\(0\),没有意义可以直接结束了。)我们只需要精细实现一下,复杂度就是\(\log_2{V}\sum_{i=1}^{i \le \log_2{n}}{\lfloor \frac{n}{2^i} \rfloor i \approx n \log_2{V} }\)(这里\(i\)表示线段树上这一个点统计的范围是\(2^i\)),总复杂度就是\(O(q \log^2_2{n}+n\log_2{V})=O(q\log^2_2{n})\)的复杂度了,卡一卡常就可以通过。
代码:
#include <bits/stdc++.h>
#define il inline
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)>>1)
using namespace std;
typedef __uint128_t u128;
const int maxn=3e5+10;
inline 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;
}
inline void output(u128 res) {
if(res >= 16)
output(res / 16);
putchar(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16);
}
bool o[maxn<<2];
u128 tag[maxn<<2];
vector<u128>val[maxn<<2];
u128 sum,a[maxn],x,mx,dw=1,ans;
int n,m,L,R,op,ns;
void add_tag(int id,u128 v){
tag[id]&=v;
for(int i=0;i<val[id].size();i++){
val[id][i]&=v;
}
return;
}
il void push_down(int id){
if(tag[id]==mx){
return;
}
add_tag(lid,tag[id]);
add_tag(rid,tag[id]);
tag[id]=mx;
return;
}
il void push_up(int id){
u128 lin=0;
auto &l1=val[id],&l2=val[lid],&l3=val[rid];
for(int i=0;i<l2.size();i++){
l1[i]=l2[i]^l3[i]^lin;
lin=(lin&l2[i])|(lin&l3[i])|(l2[i]&l3[i]);
}
l1[l2.size()]=lin;
o[id]=o[lid]&o[rid];
return;
}
il void query(int id,int l,int r,int q,int w){
if(o[id]){
return;
}
if(q<=l&&r<=w){
for(int i=0;i<val[id].size();i++){
sum+=val[id][i]<<i;
}
return;
}
push_down(id);
if(q<=mid){
query(lid,l,mid,q,w);
}
if(w>mid){
query(rid,mid+1,r,q,w);
}
return;
}
il void add1(int id,int l,int r,int q,int w,u128 qw){
if(o[id]){
return;
}
if(l==r){
o[id]=(val[id][0]/=qw)==0;
return;
}
push_down(id);
if(q<=mid){
add1(lid,l,mid,q,w,qw);
}
if(w>mid){
add1(rid,mid+1,r,q,w,qw);
}
push_up(id);
return;
}
il void add2(int id,int l,int r,int q,int w,u128 qw){
if(o[id]){
return;
}
if(q<=l&&r<=w){
add_tag(id,qw);
return;
}
push_down(id);
if(q<=mid){
add2(lid,l,mid,q,w,qw);
}
if(w>mid){
add2(rid,mid+1,r,q,w,qw);
}
push_up(id);
return;
}
il void build(int id,int l,int r){
tag[id]=mx;
if(l==r){
val[id].push_back(a[l]);
return;
}
build(lid,l,mid);
build(rid,mid+1,r);
val[id].resize(val[lid].size()+1);
push_up(id);
return;
}
signed main(){
for(int i=0;i<128;i++){
mx=mx+(dw<<i);
}
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
a[i]=read();
}
ns=n;
n=1;
while(n<ns){
n<<=1;
}
build(1,1,n);
while(m--){
scanf("%d%d%d",&op,&L,&R);
if(op==1){
x=read();
if(x==1){
continue;
}
add1(1,1,n,L,R,x);
}
if(op==2){
x=read();
add2(1,1,n,L,R,x);
}
if(op==3){
sum=0;
query(1,1,n,L,R);
output(sum);
putchar('\n');
}
}
return 0;
}
浙公网安备 33010602011771号