zoukankan      html  css  js  c++  java
  • (ST表+二分+前缀和)CSU 1879

    题意:

    给定一个序列,求异或和按位与和相同的区间有几个。

    异或和:n个数异或起来。按位与和类似。

    分析:

    这才是神题,基础算法大杂烩。

    问大佬这题的时候,人家只说很不难啊。。

    只能说自己太菜。

    由于询问区间个数,自然要快速知道某一个区间的异或和按位与和。

    异或和很简单,利用他的性质,直接求前缀和即可。

    但是按位与没有和加法异或类似的性质,无法直接求出。

    但是利用之前的知识可以将所有结果预处理出来,而且只要nlogn的复杂度。

    就是之前搞RMQ的ST表,很适合离线查询。

    几乎不用动的把 min或max换成&,就能得到O(1)查询区间按位与和的方法。

    此时,依然无从下手。

    我们还需要知道一个性质,就是说按位与是单调递减的!

    因为二进制表示中,1不会增多,只会减少,再确切一点在32位int整数中只会减少32次,最多!

    所以最后只会出来类似999998877444422222221110000000这样的值。

    也就是说固定每一位左端点,从左端点按位与到最后一位都是出现想上面这样类似的数列。

    这时我们就能发现

    假设当前固定左端点为L,第一个9的开头是R,9的结尾是W,i为从R到W。

    异或前缀和用pre数组表示。

    那么那么如果要是异或和和按位与相等。

    必须要满足pre[i]^pre[L-1]==9

    我们需要统计的是满足这样条件的i的数量。

    但是显然不能使用暴力的方式。

    继续利用异或的性质,a^b=c  ==> a=c^b

    我们可以知道pre[i]==9^pre[L-1]

    此时我们只要找前缀和里有几个是满足这个条件的。

    这里又是一个非常牛逼的方法,直接用map<int,vector<int>>记录每一个前缀异或和的所有下标。

    还记得当年的有序数列计数操作么?

    upper_bound - lower_bound。。。。

    最终复杂度O(n*log(ai)*log(n))

    到此,所有题解都结束了,又回忆了一次,只想说好牛逼。。

    绝对好题。。

    代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <map>
     5 #include <iostream>
     6 #include <vector>
     7 
     8 
     9 
    10 using namespace std;
    11 
    12 const int inf = 0x3f3f3f3f;
    13 const int maxn = 100010;
    14 
    15 #define fi first
    16 #define se second
    17 typedef long long ll;
    18 typedef pair<int, int> pii;
    19 
    20 int n;
    21 int num[maxn];
    22 int pre[maxn];
    23 
    24 int st[maxn];
    25 int dp[maxn][20];
    26 
    27 map<int, vector<int> > xo;
    28 
    29 void init() {
    30     pre[0] = 0;
    31     xo.clear();
    32 }
    33 
    34 void Build_ST() {
    35     st[0] = -1;
    36     for(int i = 1; i <= n; i++) {
    37         st[i] = ((i & (i - 1)) == 0) ? st[i - 1] + 1 : st[i - 1];
    38         dp[i][0] = num[i];
    39     }
    40     for(int  j = 1; j <= st[n]; j++) {
    41         for(int i = 1; i + (1 << (j - 1))  <= n; i++) {
    42             dp[i][j] = dp[i][j - 1] & dp[i + (1 << (j - 1))][j - 1];
    43         }
    44     }
    45 }
    46 
    47 int ga(int l, int r) {
    48     int k = st[r - l + 1];
    49     return dp[l][k] & dp[r - (1 << k) + 1][k];
    50 }
    51 
    52 int gr(int zl, int l, int b) {
    53     int left = l, right = n;
    54     int w = l + 1;
    55     while(left <= right) {
    56         int mid = (left + right) >> 1;
    57         int pp = ga(zl, mid);
    58         if(pp == b) {
    59             left = mid + 1;
    60             w = mid;
    61         } else {
    62             right = mid - 1;
    63         }
    64     }
    65     return w;
    66 }
    67 
    68 
    69 int main() {
    70 //    freopen("hack.in", "r", stdin);
    71 //    freopen("hack.out", "w", stdout);
    72     scanf("%d", &n);
    73     init();
    74     for(int i = 1; i <= n; i++) {
    75         scanf("%d", &num[i]);
    76         pre[i] = pre[i - 1] ^ num[i];
    77         xo[pre[i]].push_back(i);
    78     }
    79     Build_ST();
    80     ll ans = 0;
    81     for(int i = 1; i <= n; i++) {
    82         int w = i - 1;
    83         int q = 2147483647;
    84         for(int r = w; r < n; r = w) {
    85             q &= num[r + 1];
    86             w = gr(i, r + 1, q);
    87             int d = pre[i - 1] ^ q;
    88             ans += upper_bound(xo[d].begin(), xo[d].end(), w) - lower_bound(xo[d].begin(), xo[d].end(), r + 1);
    89         }
    90     }
    91     printf("%lld
    ", ans);
    92     return 0;
    93 
    94 }
  • 相关阅读:
    畅通工程续
    find the safest road
    Window Pains
    什么是DO / DTO / BO / VO /AO ?
    编程四大件
    1.Redis简介和安装
    0.Redis课程大纲
    8.docker容器虚拟化与传统虚拟机比较
    7.docker私有仓库
    6.Docker服务编排
  • 原文地址:https://www.cnblogs.com/tak-fate/p/6789783.html
Copyright © 2011-2022 走看看