广东省赛G. Swapping Operation

#include<bits/stdc++.h>
#define ll long long
#define all(a) (a).begin(),(a).end()
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;

/*
链接 : https://codeforces.com/gym/104369/problem/G

题意 : 求 F(A) = max((a[1] & a[2] & ... & a[k]) + (a[k + 1] & a[k + 2] & ... & a[n]))
        最多可以进行一次交换 a 中两个元素的位置

分析 :令 f(i, j) 表示 a[i] & a[i + 1] & ... & a[j]
      假设 k 固定,对于前缀 f(1, k) 中至多有 log 个关键点 i 使得 f(1, i - 1) != f(1, i)
      对于后缀 f(k + 1, n) 也至多有 log 个位置 j 使得 f(j + 1, n) != f(j, n)
      
      假设交换了两个非关键点,因为移走非关键点后值不会发生改变,
      加入另一个非关键点还可能导致整体值变小,所以这样的交换是没有意义的

      假设交换了两个关键点,因为前后缀关键点都至多 log 个,暴力枚举 k 和前后缀关键点即可,复杂度 O(nlog^2)

      假设交换了一个关键点和一个非关键点,以前缀关键点 i 和后缀非关键点 j 为例
      后缀的值为 f(k + 1, n) & a[i] 也就是说如果 a[i] 确定,则 后缀的值也确定
      此时只需要找出一个非关键点 j 使得 f(1, i - 1) & a[j] & f(i + 1, k) 最大即可
      f(i + 1, k) 的值只有 log 种,对于 f(i + 1, k),若 i 固定,则 k 越小越好,因为 k 越小可以选择的 j 越多
      所以只需要暴力枚举 i 和 k,对于新的值 f(i + 1, k) 再暴力枚举 j 即可,复杂度 O(nlog^2)
*/ 

int n, a[N];
int f[21][N], Log[N];
void init_ST()
{
    for(int i = 1; i <= n; i++) f[0][i] = a[i];

	for (int j = 1; j <= 20; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			f[j][i] = (f[j - 1][i] & f[j - 1][i + (1 << (j - 1))]);
	Log[1] = 0;
	for (int i = 2; i <= n; i++)
		Log[i] = Log[i / 2] + 1;
}
int query(int l, int r)
{
    if(l > r) return (1 << 30) - 1;
	int k = Log[r - l + 1];
	return f[k][l] & f[k][r - (1 << k) + 1];
}

int ans;
map<array<int, 2>, int> vis;
void cal_pre(int i, int val, int suf_val, int k)
{
    if(vis.count({val, i})) return;
    vis[{val, i}] = 1;
    int mx = 0;
    for(int j = k + 1; j <= n; j++) mx = max(mx, val & a[j]);
    ans = max(ans, mx + suf_val);
}
void cal_suf(int j, int val, int pre_val, int k)
{
    if(vis.count({j, val})) return;
    vis[{j, val}] = 1;
    int mx = 0;
    for(int i = 1; i <= k; i++) mx = max(mx, val & a[i]);
    ans = max(ans, pre_val + mx);
}

void solve()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    
    init_ST();

    ans = 0;
    for(int k = 1; k < n; k++) ans = max(ans, query(1, k) + query(k + 1, n));

    vector<int> pre, suf;
    for(int i = 1; i < n; i++) if(query(1, i - 1) != query(1, i)) pre.push_back(i);
    for(int i = n; i > 1; i--) if(query(i + 1, n) != query(i, n)) suf.push_back(i);

    for(int k = 1; k < n; k++)
    {
        for(auto &i : pre)
        {
            if(i > k) break;
            for(auto &j : suf)
            {
                if(j <= k) break;
                int res1 = (query(1, i - 1) & a[j] & query(i + 1, k));
                int res2 = (query(k + 1, j - 1) & a[i] & query(j + 1, n));
                ans = max(ans, res1 + res2);
            }
        }
    }

    pre.clear();
    for(int k = 1; k < n; k++)
    {
        if(query(1, k - 1) != query(1, k)) pre.push_back(k);
        for(auto &i : pre) cal_pre(i, query(1, i - 1) & query(i + 1, k), query(k + 1, n) & a[i], k);
    }

    vis.clear();
    suf.clear();
    for(int k = n - 1; k >= 1; k--)
    {
        if(query(k + 1, n) != query(k + 2, n)) suf.push_back(k + 1);
        for(auto &j : pre) cal_suf(j, query(k + 1, j - 1) & query(j + 1, n), query(1, k) & a[j], k);
    }

    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while(t--) solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值