1 条题解
-
0
思路
一次插入只会发生在某个原始间隙 ( 与 之间)内部:新元素插进这条间隙后,后续操作可以继续作用在这条间隙里的相邻元素上,而不同间隙之间互不影响。因此只需分别求出每一对相邻数 能生成哪些新值,再把这些生成值和原数组元素一起放进
set去重,set的大小就是答案。下面说明一对 最多只能生成 个不同的值。与、或、异或都是按位独立运算,所以生成的每个数都是 的某个按位表达式;又因为
或运算可以由异或和与表示,于是所有能生成的数都是不含常数项的多重线性多项式,只有以下 种可能:
$$0,\quad x,\quad y,\quad x\land y,\quad x\oplus y,\quad x\lor y,\quad x\land(y\oplus x),\quad y\land(x\oplus y)$$其中 取出" 为 而 为 "的那些位, 取出" 为 而 为 "的那些位, 对应 。
所以对每个间隙做一次 DFS 枚举即可:
dfs(l, r, c)表示对相邻的 执行第 种操作( 异或、 与、 或),算出结果 后插入集合;若 与 或 相等,说明插入的是重复值,不会产生新的相邻关系,直接剪枝返回;否则继续对 和 递归。递归时的扩展方式需要注意:只有异或操作可以"向左扩展",即继续对 做异或;而
dfs(t,r,1)相当于用异或做逆运算,不属于合法的插入操作,因此不做。与、或操作则对左右两侧都继续扩展。候选值最多 个,每个间隙的搜索规模是常数,所以整体规模只与输入长度线性相关。
时间复杂度 ,空间复杂度 (
set中存答案)。代码
#include<bits/stdc++.h> #define int long long using namespace std; int n; set<int>s; // 所有出现过的数(原数组元素 + 插入生成的值) // l:左边的数 r:右边的数 c:操作类型 1:异或 2:与 3:或 void dfs(int l,int r, int c){ int t = 0; if(c==1){t = l^r;} else if(c==2){t = l&r;} else{t = l|r;} s.insert(t); // 记录新生成的值 if(t==l or t==r){return;} // 新插入的数和原来的数相等,不会产生新的相邻关系,剪枝 if(c!=1){// 不是异或操作时,还可以继续对它做异或扩展 dfs(l,t,1); // 不能 dfs(t,r,1),那样相当于做异或的逆运算,不是合法插入 } // 继续扩展与和或操作 dfs(l,t,2); dfs(t,r,3); } void go(){ cin>>n; vector<int>a(n+1,0); for(int i=1;i<=n;i++){ cin>>a[i]; s.insert(a[i]); // 原数组元素本身也算 } for(int i=1;i<n;i++){ for(int j=1;j<=3;j++){// 枚举相邻两个数之间的三种操作 dfs(a[i], a[i+1], j); } } cout<<s.size()<<endl; // 去重后的个数就是答案 } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int t=1; // 本题只有一组数据,不需要读入测试组数 while(t--){ go(); } return 0; }
- 1
信息
- ID
- 17
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者