于是今天又是一场愉悦的考试。
笨蒟蒻今天终于不爆零啦!好开心!
T1:
说白了就是要在区间内选出一个子集使之xor和为0。
直觉告诉我这东西线段树。
然而三种标记没法合并啊,怎么办啊......
莫队吧,区间修改不可做。
看80分的值域那么小,要不然,我们来倍增沃尔什变换吧。
不不不,这是什么鬼畜的东西......
算了,40分值域才15,n^2暴力+bitset可过。
好像能线性基?然而40分不需要线性基的,算了不管了暴力了。
然后下来发现大力线性基有80分......
说下正解:
显然当r-l+1>30的时候一定输出Yes(这不显然啊,我怎么没想出来)。
为什么?考虑线性基的插入过程,我们逐个插入,当某个数不能被插入时说明xor出0,符合要求。
而值域10^9代表线性基最多插30个,根据鸽巢原理,当你插入第31个数的时候要么无法插入,要么前面已经插入出0了。
然后我们当r-l+1<=30的时候大力计算即可。
所以我们有了完美的n^2暴力,能过80分。
剩下的20分怎么办呢?考虑用线段树维护修改。
还是老问题,这个标记没法合并。
你可以线段树强行push,显然这样是O(n^2)的,而且是大常数。
其实暴力推倒(不是这个字吧)一下是可以合并的,然而我比较蒻,不愿意推(H是不行的)。
于是我选择按位考虑,or变成数字为1的位强制是1,and变成数字为0位强制是0,xor变成区间01互换。
好的,我们可以每个节点开31个char去维护这个东西是吧......
然后就有了一个常数巨大的伪正解。
考场40分代码:
1 #include<bits/stdc++.h> 2 #define debug cout 3 using namespace std; 4 const int maxn=1e3+1e2,maxe=64,maxl=32; 5 typedef bitset<maxe> BitSet; 6 7 int in[maxn],n; 8 BitSet prv[maxn]; 9 10 inline BitSet trans(const BitSet &sou,const int &x) { 11 BitSet ret = sou; 12 for(int i=0;i<maxl;i++) if( sou[i] ) ret[i^x] = 1; 13 return ret; 14 } 15 16 inline void getprv(int l,int r) { 17 prv[l] &= 0; prv[l][in[l]] = 1; 18 for(int i=l+1;i<=r;i++) prv[i] = trans(prv[i-1],in[i]) , prv[i][in[i]] = 1; 19 } 20 inline bool getans(int l,int r) { 21 if( !in[l] ) return 1; 22 for(int i=l+1;i<=r;i++) if( !in[i] || prv[i-1][in[i]] ) return 1; 23 return 0; 24 } 25 26 inline void modify(int l,int r,int x,int o) { 27 if( o == 1 ) for(int i=l;i<=r;i++) in[i] &= x; 28 if( o == 2 ) for(int i=l;i<=r;i++) in[i] |= x; 29 if( o == 3 ) for(int i=l;i<=r;i++) in[i] ^= x; 30 } 31 32 int main() { 33 static int q; 34 scanf("%d",&n); 35 for(int i=1;i<=n;i++) scanf("%d",in+i); 36 scanf("%d",&q); 37 for(int i=1,o,l,r,x;i<=q;i++) { 38 scanf("%d%d%d",&o,&l,&r); 39 if( !o ) { 40 getprv(l,r); 41 puts(getans(l,r)?"Yes":"No"); 42 } else { 43 scanf("%d",&x); 44 modify(l,r,x,o); 45 } 46 } 47 return 0; 48 }