【題解】簽到題

題目大意

  給定一個長度爲\(n\)的序列\(a_1, a_2, \dots , a_n\)\(1 \leq n, a_i \leq 5 \times 10^5\))求有多少對$(i, j) $知足\(1 \leq i \leq j \leq n\)\(\gcd (a_i, a_{i + 1}, \dots , a_j) \oplus (a_i \lor a_{i + 1} \lor \dots \lor a_j) = k\)ios

題解

  容易想到,當\(i\)不變時,隨着\(j\)從小變大,區間\(\gcd\)的值只會變小,且由於每次都要除以一個質因子,因此最多變小\(\log\)次就到\(1\),同時區間或的值只會變大,且由於每次都會有一個二進制位變化,因此最多變大\(\log\)次就到最大值。
  若是咱們當前知道對於每一個\(i\),全部可能出現的區間\(\gcd\)和區間或的值,就能求出\((i,j)\)的對數。
  同時,咱們也能夠由\(i + 1\)獲得\(i\)可能出現的區間\(\gcd\)和區間或的值,因此咱們能夠從\(n\)開始枚舉到\(1\),直接求解便可。git

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>

#define MAX_N (500000 + 5)
#define SIZE (1 << 21)

#define Getchar() (p1 == p2 && (p2 = (p1 = fr) + fread(fr, 1, SIZE, stdin), p1 == p2) ? EOF : *p1++)

using namespace std;

char fr[SIZE], * p1 = fr, * p2 = fr;

void Read(int & res)
{
    res = 0;
    char ch = Getchar();
    while(!isdigit(ch)) ch = Getchar();
    while(isdigit(ch)) res = res * 10 + ch - '0', ch = Getchar();
    return; 
}

struct Interval
{
    int val;
    int r;
};

int n, k;
int a[MAX_N];
Interval g1[25], g2[25];
int tot1, tot2;
long long ans;

int gcd(int a, int b)
{
    int c;
    while(b)
    {
        c = a % b;
        a = b;
        b = c;
    }
    return a;
}

void Update(int x, Interval g[], int & tot)
{
    g[tot + 1].val = 0x7f7f7f7f;
    int d = 0;
    for(int i = 1; i + d <= tot; ++i)
    {
        g[i] = g[i + d];
        while(g[i + d].val == g[i + d + 1].val)
        {
            g[i] = g[i + d + 1];
            ++d;
        }
    }
    tot -= d;
    return;
}

int main() 
{
    Read(n); Read(k);
    for(int i = 1; i <= n; ++i)
    {
        Read(a[i]);
    }
    int t1, t2;
    g1[0].r = g2[0].r = n + 1;
    for(int i = n; i; --i)
    {
        for(int j = 1; j <= tot1; ++j)
        {
            g1[j].val = gcd(g1[j].val, a[i]);
        }
        for(int j = 1; j <= tot2; ++j)
        {
            g2[j].val |= a[i];
        }
        g1[++tot1] = g2[++tot2] = (Interval){a[i], i};
        Update(i, g1, tot1);
        Update(i, g2, tot2);
        t1 = tot1;
        t2 = tot2;
        while(t1 && t2)
        {
            if((g1[t1].val ^ g2[t2].val) == k) 
            {
                ans += min(g1[t1 - 1].r, g2[t2 - 1].r) - max(g1[t1].r, g2[t2].r);
            }
            if(g1[t1 - 1].r <= g2[t2 - 1].r) --t1;
            else --t2;
        }
    }
    printf("%lld", ans);
    return 0;
}
相關文章
相關標籤/搜索