1. 程式人生 > 實用技巧 >【洛谷5610】[Ynoi2013] 大學(並查集)

【洛谷5610】[Ynoi2013] 大學(並查集)

\(\Large\texttt{CF617E}\)

\(\small\texttt{In my Blog}\)

這道題其實和P4462幾乎一樣,但是我卻多調了30min,細節很多。洛谷資料太水了QwQ

\(\large\texttt{Meaning}\)

給定 \(n,m,k\) ,共 \(m\) 個詢問,每次詢問一個區間,問區間內有多少個子區間的異或和等於 \(k\)

\(\large\texttt{Solution}\)

題目中說是區間,則可以轉化為字首和的形式。

\(S_i=a_1~\texttt{xor}~a_2~\texttt{xor}~...~\texttt{xor}~a_i\)

\(a_l~\texttt{xor}~a_{l+1}~\texttt{xor}~a_{l+2}~...~\texttt{xor}~a_r=S_r~\texttt{xor}~S_{l-1}\)

然後每次詢問就可轉化為在 \([l-1,r]\) 之間有多少個 \(S_i\)\(S_j\) \((i\le j)\) 使得 \(S_i~\texttt{xor}~S_j=k\)

發現這個可以用莫隊離線下來做,因為 \(S_i~\texttt{xor}~S_j=k\)\(S_i~\texttt{xor}~k=S_j\)\(S_i\) 數量改變,答案也就相應改變了。

幾個要注意的地方,坑點:

  1. 題目中沒保證 \(k\) 不為 \(0\) , 即統計貢獻的時候要明確,先改貢獻,還是先改個數

  2. 陣列的上界定為 \(2e6\) 不是 \(1e6\), 我也不知道為啥。

然後處理好細節後,就能過了。

\(\large\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 2e6; //細節2
inline int read()
{
    register int s = 0;
    register bool neg = 0;
    register char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        neg |= (c == '-');
    for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
        ;
    return (neg ? -s : s);
}

int a, b, c, s[N + 10], bel[N + 10], p[N + 10], ans, Ans[N + 10];
struct node
{
    int l, r, id;
    bool operator<(const node &t) const
    {
        return (bel[l] ^ bel[t.l]) ? bel[l] < bel[t.l] : (bel[l] & 1 ? r < t.r : r > t.r);
    }
} ask[N + 10];

inline void add(int n)
{
    ans += p[(n ^ c)]; //細節1
    p[n]++;
}

inline void del(int n)
{
    p[n]--; //細節1
    ans -= p[(n ^ c)];
}

signed main()
{
    a = read();
    b = read();
    c = read();
    int k = (int)(sqrt(a));
    for (int i = 1; i <= a; i++)
        bel[i] = (i - 1) / k + 1;
    for (int i = 1; i <= a; i++)
        s[i] = (read() ^ s[i - 1]);
    for (int i = 1; i <= b; i++)
    {
        ask[i].l = read() - 1;
        ask[i].r = read();
        ask[i].id = i;
    }
    sort(ask + 1, ask + b + 1);
    // p[0]= 1;
    int l = 1, r = 0; //其實這裡左端點必須設為1,如果設為0,則要先將0這個位置計數1.
    for (int i = 1; i <= b; i++)
    {
        while (l < ask[i].l)
            del(s[l++]);
        while (l > ask[i].l)
            add(s[--l]);
        while (r < ask[i].r)
            add(s[++r]);
        while (r > ask[i].r)
            del(s[r--]);
        Ans[ask[i].id] = ans;
    }
    for (int i = 1; i <= b; i++)
        printf("%lld\n", Ans[i]);
    return 0;
}