1. 程式人生 > >LOJ6469 Magic(trie+)

LOJ6469 Magic(trie+)

題目連結

紀念我菜的真實的一場模擬賽

首先看到這個題目,一開始就很毒瘤。一定是沒有辦法直接做的。

我們考慮轉化問題
假設,我們選擇列舉 x x ,其中 x x 10

10 的若干次方,那麼我們只需要求有多少對異或完比 x x 大的數,那麼就是 x x 對於答案的貢獻了。

那麼應該怎麼求比多少對呢?

!!!trie!!!

對於trie的每個節點,我們維護他的子樹裡面的數的個數,記為 s i z e [ x ] size[x]

我們考慮把每一個串放進trie裡面去跑,如果當前這個數的這一位是1,而10的幾次方對應的也是1的話,那麼當前位只能選擇0,即 r o o t = c h [ r o o t ] [ 0 ] root=ch[root][0] ,如果10的幾次方對應的位是0的話,那麼這一位選擇0一定是全都滿足條件的,是1的不一定,那麼我們可以把0的那邊記錄進答案裡面,然後走1的那邊試一試, a n s = a n s + c h [ r o o t ] [ 0 ] , r o o t = c h [ r o o t ] [ 1 ] ans=ans+ch[root][0],root=ch[root][1]

另一種情況同理

不過需要注意的是,因為我們的貪心的放,所以必須從高位到低位來迴圈

int query(int now,int lim)
{
 int root=1;
 int ans=0;
 for (register int i=62;i>=0;--i)
 {
  if (!root) break;
  if (now&(1ll << i))
  {
   if (lim & (1ll << i))
     root=ch[root][0];
   else
     ans=ans+sum[ch[root][0]],root=ch[root][1];
  }
  else
  {
   if (lim&(1ll <<i))
     root=ch[root][1];
   else
     ans=ans+sum[ch[root][1]],root=ch[root][0];
  }
 }
 return ans;
}

對於每一個,我們都這麼算,那麼最後的 a n s / 2 ans/2 ,就是我們要的答案
因為每一對,我們會重複算兩遍

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 1e5+1e2;
int n;
int a[maxn];
int ch[7000000][3];
int tot=1;
int ans;
int sum[7000000];
void insert(int now)
{
 int root=1;
 for (register int i=62;i>=0;--i)
 {
    int x = (now & (1ll << i));
    if (x!=0) x=1;
    else x=0;
    if (!ch[root][x]) ch[root][x]=++tot;
    root=ch[root][x];
    sum[root]++;
 }
 //cout<<tot<<endl;
}
int query(int now,int lim)
{
 int root=1;
 int ans=0;
 for (register int i=62;i>=0;--i)
 {
  if (!root) break;
  if (now&(1ll << i))
  {
   if (lim & (1ll << i))
     root=ch[root][0];
   else
     ans=ans+sum[ch[root][0]],root=ch[root][1];
  }
  else
  {
   if (lim&(1ll <<i))
     root=ch[root][1];
   else
     ans=ans+sum[ch[root][1]],root=ch[root][0];
  }
 }
 return ans;
}
int qsm(int i,int j)
{
 //if (j==0) return 0;
 int ans=1;
 while (j)
 {
  if (j&1) ans=ans*i;
  i=i*i;
  j>>=1;
 }
 return ans;
}
signed main()
{
  n=read();
  for  (int i=1;i<=n;++i) a[i]=read();
  for (register int i=1;i<=n;++i) insert(a[i]);
  int pre=0;
  for (register int i=0;i<=18;++i)
  {
     int cnt=0;
     for (register int j=1;j<=n;++j)
     {
       cnt=cnt+query(a[j],qsm(10,i)-1);
       //cout<<i<<" "<<cnt<<endl;
   }
   ans=ans+cnt;
  }
  cout<<ans/2;
  return 0;
}