• 1054D Changing Array 【位运算+思维】


    题目:戳这里

    题意:两个数n,k,满足给的n个数小于2的k次方。每个数可以进行一次操作,即把a[i]换成a[i]^(1<<k-1);求最多的连续区间数使得 区间[L,R] (1<=L<=R<=n),满足: a[L] ^ a[L+1] ^ … ^ a[R-1] ^ a[R] != 0

    解题思路:

    首先我们知道n个数可构成的连续区间是n*(n+1)/2个,如果一个一个找肯定会超时,需要一种能快速算出[L,R]区间异或和的方法。因为异或满足交换法则。所以a[1]^a[2]^...^a[L-1] ^ a[1]^a[2]^...^a[R]=a[L]^a[L+1]^...^a[R];也就是只要纪录前缀异或和,就可以快速算出每个区间的异或值。

    设前缀异或和为s[],maxx=1<<k-1。

    因为相等的值相互异或为0,所以只要s[L]==s[R],则[L,R]区间的异或和为0. 此外,假设对a[i]进行一次异或操作得a[i]'=a[i]^maxx,则s[i]'=s[i]^maxx,因为异或满足s[i]^maxx^maxx=s[i],所以对于s[]数组,每进行两次^maxx的操作,当前的前缀值s[i]就相当于没有进行操作。

    这样问题就转化为,经过无数次操作后,使得s[]数组中相等的数尽量的少,就可以用贪心解决了。

    用map<ll,ll>mp维护每个值出现的次数。计算前缀和s[i]值时,决策s[i]=mp[s[i-1]]>mp[s[i-1]^maxx]?s[i-1]^maxx:mp[s[i-1]];用基础的组合知识(确定L,R两端点便可以确定一个区间)就可以算出最终答案。

    附ac代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn = 2e5 + 10;
     5 const ll mod = 998244353;
     6 ll a[maxn];
     7 int main() {
     8 
     9     ll n, k;
    10     scanf("%lld %lld", &n, &k);
    11     for(int i = 1; i <= n; ++i) {
    12         scanf("%lld", &a[i]);
    13     }
    14     ll ans = ll((n + 1) * n / 2);
    15     ll maxx = 1ll;
    16     maxx = (maxx << k) - 1ll;
    17     map<ll,ll> mp;
    18     mp[0] = 1;
    19     ll now = 0;
    20     for(ll i = 1; i <= n; ++i) {
    21         now ^= a[i];
    22         now = mp[now]>mp[now ^ maxx]?now^maxx:now;
    23         ans -= mp[now];//组合数Cn2
    24         mp[now]++;
    25     }
    26     printf("%lld
    ", ans);
    27     return 0;
    28 }
    View Code
  • 相关阅读:
    Myeclipse下使用Maven搭建spring boot项目
    Dubbo+Zookeeper视频教程
    dubbo项目实战代码展示
    流程开发Activiti 与SpringMVC整合实例
    交换两个变量的值,不使用第三个变量的四种法方
    数据库主从一致性架构优化4种方法
    数据库读写分离(aop方式完整实现)
    在本地模拟搭建zookeeper集群环境实例
    box-sizing布局
    盒子模型
  • 原文地址:https://www.cnblogs.com/zmin/p/9909695.html
Copyright © 2020-2023  润新知