9.29T3 前缀操作+数位贪心
小清新最优化(easy)
【题目背景】
NOIP2018 即将到来,一个新的轮回就要开始。
新时代的 BSOIer:加油,未来是 你们的!
【题目描述】
给出一个长度为 n 的序列,序列的每个元素为一个二元组,代表一种单目运算:
• (0,x): 对于一个数 a,将其变为 a & x。( &=x)
• (1,x): 对于一个数 a,将其变为 a | x。( |=x)
现在给出 q 次询问,每次询问包含四个整数 l,r, L, R,其中 1 ≤ l ≤ r ≤ n, 0 ≤ L ≤ R ≤ 2^31 − 1
表示对于在 [L,R] 之间的整数 x,按照序列中第 l 到第 r 项从左到右结合的 顺序进行运算,求可能的最大的运算结果。
【输入格式】
从文件 easy.in 中读入数据。
输入的第一行包含一个正整数 n,保证 n ≤ 1000000,表示树的节点总数。
接下来 n 行,每行两个整数,表示这个序列。 接下来一行包含一个整数 q,表示有 q 次询问。
接下来 q 行,每行包含四个整数 l,r, L, R。
【输出格式】
输出到文件 easy.out 中。
对于每个询问输出一行表示可能的最大运算结果。
【样例 1 输入】
10
1 49
1 58
0 72
0 78
1 9
0 65
0 42
1 3
1 29
0 12
10
4 10 9 57
1 4 78 99
6 7 26 97
3 8 10 33
10 10 21 79
3 8 36 93
6 6 28 91
5 8 1 53
5 9 68 90
5 7 3 30
【样例 1 输出】
12
72
0
3
12
3
65
3
31
0
【样例 1 解释】
不难验证结果的正确性。
【样例 2】
见选手目录下的 easy/easy2.in 与 easy/easy2.ans。
【子任务】
这道题把我坑了。。。我上来就直接开始写2棵线段树维护区间的与操作和或操作,写完了老是调不过数据,最后我才发现位运算不满足交换律,于是我成功GG爆零了
NOI2018金牌获得者,中国国家集训队AChen这么说:
fu*k。。。。
上正解:
首先我们要知道对于每一位来说 & 1和 | 0是没有任何意义的,顶多只是凑一个数
然后我们就知道其实对于一位的一个操作来说,&0相当于赋值0,|1相当于赋值1
知道这一点实际上我们就可以开始进行我们的前缀操作了
如果遇到赋值操作我们就直接统计一下就是前缀了,这个前缀只和最后一个赋值操作有关系,显然分为0,1,还有一个是-1,代表根本就没起赋值的区间
我们现在开始考虑部分分
如果是没有上限的话,那么意思就是我们每一位取什么都是没有关系的
所以尽量取1就可以了
如果是没有下限的话,我们要分两种情况来讨论:
1.如果不是赋值操作,这就代表我们实际上就可以通过更改来改变我们的决策,那么也就是放0和1的问题
显然如果我们不超过的情况下肯定是要选1,当然如果超过了还是乖乖的选择0
2.如果我们是赋值操作,那么选什么其实都没有什么影响,但是我们为了考虑从高位到低位要尽量高的情况下,因为当前位如果选1不会大于R,
那么选0的话后面无论选什么对我们都没有什么关系,因为是始终小于上界的
这样我们就把部分分就得到了
现在我们再次考虑没有限制条件的情况下,拥有了上界和下界
自然不用说两个数的位数应该要对齐,不够就补上
显然如果我们对此唯一的一个问题就在于万一小于或者大于了又怎么办,我们可以拆分成两个部分来解决:前面二进制数相同的部分和后面不同的部分
显然如果一个数比另一个数要大的话,肯定是从某一位开始比上界要小
至于相同的部分直接赋值相同就可以了
现在我们来到了不同的地方,显然肯定是下界0,上界1
我们看在这一位里面的区间操作是什么,如果是0或1赋值操作的话,那么就直接赋值0,这样后面就全部是1,这样出来的结果一定是最大的,同时这个数还永远小于上界
如果是-1的话我们就看一下能不能选1,如果不能选的话直接填上0,后面填上1就可以了
如果可以选1 的话后面还要继续判断,直到可以填0为止
所以这么一搞答案就很显然的出来了
OTZ,国家集训队原创题就这么厉害OTZ。。。NOIP模拟了两天下来只考了40分,今天第一题数组开小了直接就RE了。反思一下实际上自己的水平还远远不够。。。。
我太菜了QAQ TAT
code还没有。。。一会写完来补。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=1e6+5; 4 int f[35][MAXN]; 5 int A[MAXN]; 6 int main() 7 { 8 freopen("easy.in","r",stdin); 9 freopen("easy.out","w",stdout); 10 int n; 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++) 13 { 14 int opt; 15 scanf("%d%d",&opt,&A[i]); 16 for(int k=0;k<31;k++)f[k][i]=((A[i]>>k&1)^opt)?f[k][i-1]:i; 17 } 18 int q; 19 scanf("%d",&q); 20 for(int i=1;i<=q;i++) 21 { 22 int l,r,L,R; 23 scanf("%d%d%d%d",&l,&r,&L,&R); 24 int Trans[35]; 25 for(int k=0;k<=30;k++)Trans[k]=(f[k][r]>=l)?(A[f[k][r]]>>k&1):-1; 26 int Ans=0,res=0; 27 for(int k=30;k>=0;k--) 28 { 29 if((L>>k&1)<(R>>k&1)) 30 { 31 if(Trans[k]==-1) 32 { 33 Ans+=1<<k;res+=1<<k; 34 for(int d=k-1;d>=0;d--) 35 { 36 if(Trans[d]==1)Ans+=1<<d; 37 else if(Trans[d]==-1&&res+(1<<d)<=R)res+=1<<d,Ans+=1<<d; 38 } 39 } 40 else for(int d=k;d>=0;d--)if(Trans[d])Ans+=1<<d; 41 break; 42 } 43 res+=R&(1<<k); 44 if(Trans[k]==-1)Ans+=R&(1<<k); 45 if(Trans[k]==1)Ans+=1<<k; 46 } 47 cout<<Ans<<'\n'; 48 } 49 50 return 0; 51 }
over