日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CodeForces 1616H Keep XOR Low {a^b≤x} / CodeForces gym102331 Bitwise Xor {a^b≥x}(trie树 + 计数)

發布時間:2023/12/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CodeForces 1616H Keep XOR Low {a^b≤x} / CodeForces gym102331 Bitwise Xor {a^b≥x}(trie树 + 计数) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • CodeForces 1616H Keep XOR Low
    • problem
    • solution
    • code
  • CodeForces gym102331 Bitwise Xor
    • problem
    • solution
    • code

CodeForces 1616H Keep XOR Low

problem

洛谷鏈接

solution

雖然選的是一個子集,但本質還是二元限制。

這非常類似以前做過的題目,已知 ai,ka_i,kai?,k,求滿足 ai⊕aj≤ka_i\oplus a_j\le kai?aj?kaja_jaj? 個數。

這是一元限制,常見解法是在 trie\text{trie}trie 樹上統計。 緊貼 的思想。

具體而言,就像數位 dpdpdp 一樣,貼著 xxx 二進制位的要求走,如果 ddd 位為 111,那么直接統計 trie\text{trie}trie 樹該位 000 子樹的個數,然后繼續往 111 那邊走。

我們考慮采用同樣的方法來解決本題。只不過此時的 aia_iai? 同樣未知。

直接設計 dfs(x,y,d):dfs(x,y,d):dfs(x,y,d): 目前在 ddd 二進制位,從 x,yx,yx,y 子樹中選取子集,僅考慮 x?yx-yx?y 間限制的方案數,且前 ddd 位都是緊貼的。

首先得從高到低考慮二進制位。初始調用函數 dfs(1,1,30)dfs(1,1,30)dfs(1,1,30)。(插入以及統計子樹內個數就略過了)

  • x=yx=yx=y

    • kkkddd 位為 111

      直接返回 dfs(lson[x],rson[x],d?1)dfs(lson[x],rson[x],d-1)dfs(lson[x],rson[x],d?1)

    • kkkddd 位為 000

      返回 dfs(lson[x],lson[x],d?1)+dfs(rson[x],rson[y],d?1)dfs(lson[x],lson[x],d-1)+dfs(rson[x],rson[y],d-1)dfs(lson[x],lson[x],d?1)+dfs(rson[x],rson[y],d?1)

      這里不能乘法亂來,否則就會包含 xxx 左右兒子組合等等,這些異或在該位可是 111,超過了 kkk 限制。

  • x≠yx\ne yx?=y

    • kkkddd 位為 111

      返回 dfs(lson[x],rson[y],d?1)?dfs(rson[x],lson[y],d?1)dfs(lson[x],rson[y],d-1)*dfs(rson[x],lson[y],d-1)dfs(lson[x],rson[y],d?1)?dfs(rson[x],lson[y],d?1)

      實際上應該是兩邊都要 +1+1+1 再乘,表示僅從 x,yx,yx,y 的各一個兒子中選取的方案數,直接乘就是要求 x,yx,yx,y 左右兒子中都要選擇了。

      最后還要 ?1-1?1,減去兩邊沒一個選的空集。

      這個乘法就包含了 xxx 左右兒子組合,yyy 左右兒子組合,x,yx,yx,y 左/右兒子組合等等的可能。

      這些組合在這一位的異或都是 000,都是松弛在限制下面的。

    • kkkddd 位為 000

      返回 dfs(lson[x],lson[y],d?1)+dfs(rson[x],rson[y],d?1)dfs(lson[x],lson[y],d-1)+dfs(rson[x],rson[y],d-1)dfs(lson[x],lson[y],d?1)+dfs(rson[x],rson[y],d?1)

      但這里沒有考慮到只選 xxx 內部的左右子樹和只選 yyy 內部的左右子樹的情況。

      直接加上 (2siz[lson[x]]?1)(2siz[rson[x]]?1)+(2siz[lson[y]]?1)(2siz[rson[y]]?1)(2^{siz[lson[x]]}-1)(2^{siz[rson[x]]}-1)+(2^{siz[lson[y]]}-1)(2^{siz[rson[y]]}-1)(2siz[lson[x]]?1)(2siz[rson[x]]?1)+(2siz[lson[y]]?1)(2siz[rson[y]]?1)

      為什么這里可以直接乘了,而不是繼續 dfsdfsdfs 下去呢?

      注意到如果 x≠yx\neq yx?=y,那么之前必定是有一個更高位使得他們分叉。

      而分叉的條件都是那個更高位上的 kkk111

      所以這里可以直接 x/yx/yx/y 子樹內部各自亂選,反正異或起來至少在那個較高位都是 000

      一定是被限制松弛的方案數。

code

#include <bits/stdc++.h> using namespace std; #define int long long #define mod 998244353 #define maxn 150005 int n, m, root, cnt; int a[maxn], mi[maxn], l[maxn * 30], r[maxn * 30], siz[maxn * 30];void insert( int &now, int d, int x ) {if( ! now ) now = ++ cnt;siz[now] ++;if( ! ~ d ) return;if( x >> d & 1 ) insert( r[now], d - 1, x );else insert( l[now], d - 1, x ); }int dfs( int x, int y, int d ) {//-1是減去空集的情況數if( ! x ) return mi[siz[y]] - 1;if( ! y ) return mi[siz[x]] - 1;if( x == y ) {if( ! ~ d ) return mi[siz[x]] - 1;else if( m >> d & 1 ) return dfs( l[x], r[x], d - 1 );else return ( dfs( l[x], l[x], d - 1 ) + dfs( r[x], r[x], d - 1 ) ) % mod;}else {if( ! ~ d ) return mi[siz[x]] * mi[siz[y]] % mod - 1;//+1乘是有可能不選這個子樹中的子集else if( m >> d & 1 ) return ( dfs( l[x], r[y], d - 1 ) + 1 ) * ( dfs( r[x], l[y], d - 1 ) + 1 ) % mod - 1;else {int ans = ( dfs( l[x], l[y], d - 1 ) + dfs( r[x], r[y], d - 1 ) ) % mod;( ans += ( mi[siz[l[x]]] - 1 ) * ( mi[siz[r[x]]] - 1 ) % mod ) %= mod;( ans += ( mi[siz[l[y]]] - 1 ) * ( mi[siz[r[y]]] - 1 ) % mod ) %= mod;//這種情況計數是左右子樹一定都選了的return ans;}} }signed main() {scanf( "%lld %lld", &n, &m ); mi[0] = 1;for( int i = 1, x;i <= n;i ++ ) {scanf( "%lld", &x ), insert( root, 30, x );mi[i] = ( mi[i - 1] << 1 ) % mod;}printf( "%lld\n", ( dfs( root, root, 30 ) + mod ) % mod );return 0; }

還有一道類似的題目,只不過限制變為了 ai⊕aj≥ka_i\oplus a_j\ge kai?aj?k。但做法就完全不一樣了。

CodeForces gym102331 Bitwise Xor

problem

CF鏈接

solution

一堆數按從高到低二進制考慮,按第 www 位分為 0/10/10/1 兩個集合,第 www 位產生貢獻肯定是兩個數隸屬不同的集合。

如此遞歸分層下去,我們發現:從小到大排序后只用考慮相鄰兩個數是否滿足限制。

上述結論有一個數學化的表達:a<b<c?min?(a⊕b,b⊕c)≤a⊕ca<b<c\Rightarrow \min(a\oplus b,b\oplus c)\le a\oplus ca<b<c?min(ab,bc)ac

可以大概理解為數相差越大,二進制位的高位越有可能異或為 111,異或結果越有可能更大。

因此可以將 aaa 從小到大排序,設 fi:aif_i:a_ifi?:ai? 結尾的序列的方案數。

轉移利用 trie\text{trie}trie 樹的緊貼思想去求與 aia_iai? 異或 ≥x\ge xxaja_jaj? 對應的方案數。

code

#include <bits/stdc++.h> using namespace std; #define int long long #define mod 998244353 #define maxn 300005 int n, m; int a[maxn], cnt; int trie[maxn * 65][2], sum[maxn * 65];void insert( int x, int val ) {for( int i = 60, now = 0;~ i;i -- ) {int k = x >> i & 1;if( ! trie[now][k] ) trie[now][k] = ++ cnt;now = trie[now][k];( sum[now] += val ) %= mod;} }int query( int x ) {int ans = 0, now = 0;for( int i = 60;~ i;i -- ) {int k = x >> i & 1;if( m >> i & 1 ) now = trie[now][k ^ 1];else ( ans += sum[trie[now][k ^ 1]] ) %= mod, now = trie[now][k];if( ! now ) break; //很有可能這個數比之大的不存在 那么now就會回到0 如果繼續走下去就岔了}return ( ans + sum[now] ) % mod; }signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );sort( a + 1, a + n + 1 );int ans = 0; //f[i]=1+\sum_{j,a_i^a_j>=m}f[j]for( int i = 1;i <= n;i ++ ) {int val = query( a[i] ) + 1; //+1是自己單獨一個的方案ans = ( ans + val ) % mod;insert( a[i], val );}printf( "%lld\n", ans );return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的CodeForces 1616H Keep XOR Low {a^b≤x} / CodeForces gym102331 Bitwise Xor {a^b≥x}(trie树 + 计数)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。