JZOJ4828. 【GDOI2017模拟10.30】最大值 分类讨论+分治处理子集问题
2016-10-30 20:25
423 查看
题目大意
给定一个包含n个正整数的序列a,以及一个运算操作符op,要求你输出aiopaj(1≤j≤n)结果中的最大值。运算符op可以是and,or,xor。有t组测试数据。
n≤105
t≤6
ai≤220
解题思路
这题的弱化版是可以用根号算法过的,可是有以这题的ai较大,所以根号算法的复杂度过不去。那么考虑对于三种不同的运算符分开讨论,并且统计每一位的贡献。1.op=xor:这种情况是三种运算符中最简单的,对于每个ai,二进制的每一位都有最优取值(即选0或选1肯定有一个更优),所以只需把trie树构出来贪心的跑就可以了。
2.op=and:对于and的情况,当ai中的某一位等于0时,对于0和1的贡献是一样的,所以不能直接在trie上跑。转化一下思路,我们只考虑受到限制为二进制位,即等于1的位置,这里是可以贪心选1的。现在问题就转化为了,我们要限制一个二进制数的其中几位是1,当前位能否是1。设fx表示在a序列中存在多少个数二进制中1所在的位置包含x二进制中1的位置。
考虑如何求得f数组,我们可以用分治的思想,把[0,221−1]的数分成[0,220−1]和[220,221−1]讨论,假设我们已经分别处理好了两边的f数组,但还未考虑后面区间对前面区间的贡献,那么我们只需令fx+=fx+220(x∈[0,220−1])即可!因为fx的转移可以按是否包含220这位讨论,结果就是这两个数加起来,对于两个子区间的计算,递归进去就可以了。那么最后我们判断时只需判断fx是否大于1即可(还有ai本身)。
3。op=or:与op=and的情况一样,考虑一下细节即可,在这里不详述。
程序
//YxuanwKeith #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 1e5 + 5, MAXM = 1 << 21; struct Node { int go[2]; } tr[MAXN * 4]; int n, k, top, a[MAXN], d[25], f[MAXM + 5]; void solve(int l, int r, int bit) { if (l == r) return; int mid = (l + r) >> 1; solve(l, mid, bit - 1), solve(mid + 1, r, bit - 1); for (int i = mid + 1; i <= r; i ++) f[i - (1 << bit)] += f[i]; } void prepare() { memset(f, 0, sizeof f); for (int i = 1; i <= n; i ++) f[a[i]] ++; solve(0, MAXM - 1, 20); } void div(int x) { top = 0; memset(d, 0, sizeof d); for (; x; x >>= 1) d[top ++] = x % 2; } void solve_xor() { memset(tr, 0, sizeof tr); int root = 1, tot = 1, ans = 0; for (int i = 1; i <= n; i ++) { div(a[i]); for (int j = 20, now = root; j + 1; j --) { if (!tr[now].go[d[j]]) tr[now].go[d[j]] = ++ tot; now = tr[now].go[d[j]]; } int get = 0; for (int j = 20, now = root; j + 1; j --) { if (tr[now].go[d[j] ^ 1]) { now = tr[now].go[d[j] ^ 1]; get += 1 << j; } else now = tr[now].go[d[j]]; } ans = max(get, ans); } printf("%d\n", ans); } void solve_and() { int ans = 0; for (int i = 1; i <= n; i ++) { div(a[i]); int get = 0; for (int j = 20; j + 1; j --) { if (!d[j]) continue; if (f[get + (1 << j)] > 1) get += (1 << j); } ans = max(ans, get); } printf("%d\n", ans); } void solve_or() { int ans = 0; for (int i = 1; i <= n; i ++) { div(a[i]); int get = 0, now = 0; for (int j = 20; j + 1; j --) { if (d[j]) { get += (1 << j); continue; } if (f[now + (1 << j)] > 0) get += (1 << j), now += (1 << j); } ans = max(ans, get); } printf("%d\n", ans); } void solve() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i ++) scanf("%d", &a[i]); prepare(); if (k == 1) solve_and(); if (k == 2) solve_xor(); if (k == 3) solve_or(); } int main() { int t; scanf("%d", &t); for (int i = 1; i <= t; i ++) solve(); }
相关文章推荐
- 【jzoj5215】【BZOJ4870】【Shoi2017】【GDOI2018模拟7.9】【组合数问题】【矩阵快速幂】
- 【jzoj5231】【NOIP2017模拟A组模拟8.5】【序列问题】 【分治】
- 【jzoj5055】【GDOI2017模拟二试4.12】【树上路径】【点分治】
- JZOJ4829. 【GDOI2017模拟10.30】独木桥 根据性质转化模型后二分答案
- JZOJ 5050. 【GDOI2017模拟一试4.11】颜色树
- 【JZOJ5055】【GDOI2017模拟二试4.12】树上路径
- 【jzoj5078】【GDOI2017第三轮模拟day2】【魔法咒语】【ac自动机】【矩阵快速幂】
- {题解}[jzoj5050]【GDOI2017模拟一试4.11】颜色树
- 【JZOJ5073】【GDOI2017第三轮模拟day1】影魔
- JZOJ 5052. 【GDOI2017模拟二试4.12】旅游路线
- [jzoj5065][GDOI2017第二轮模拟day two] 开房
- [JZOJ5078].[AC自动机复习]【GDOI2017第三轮模拟day2】魔法咒语
- 【jzoj5072】【GDOI2017第三轮模拟day1】【单旋】【数据结构】
- 【jzoj5084】【GDOI2017第四轮模拟day1】【子串】【后缀数组】
- 【JZOJ5061】【GDOI2017第二轮模拟day1】最长路径
- JZOJ 5048. 【GDOI2017模拟一试4.11】IQ测试
- 【JZOJ5050】【GDOI2017模拟一试4.11】颜色树
- 【jzoj5065】【GDOI2017第二轮模拟day2】【开房间】【动态规划】
- 【JZOJ5077】【GDOI2017第三轮模拟day2】树的难题
- JZOJ 5050. 【GDOI2017模拟一试4.11】颜色树