異或相關的雜題

2022-09-19 15:51:07 字數 4281 閱讀 5147

連續多場 cf 都卡在 xor 題,並且以各不相同的方式寄了。不得不惡補一下這方面的處理技巧

codeforces 訪問有點慢,所以掛洛谷的題目鏈結

e 和 s 在玩博弈遊戲, e 先手 s 後手。

給一棵 \(n\) 個節點的樹,節點的值包含 \([1,n]\) 中的每乙個值。

e 先隨便選擇乙個點,占領它(點 \(u\) ), s 只能選擇與這個點相鄰的,沒有被占領的點(點 \(v\) )且這兩個點滿足 \(u⊕v≤\min(u,v)\)

\(⊕\) 是異或操作

現在把樹給 e 了, e 想要重新排列節點的值(樹的形狀不變,只是調換結點的值)來達到這個目的:

最大化第一輪能夠選擇的點的數量,在選了這個點之後,e 必贏。

贏:對手無路可走,你就算贏

\(u~~v\le \min(u,v)\) 顯然需要處理一下。容易發現它等價於 \(u\) 和 \(v\) 的最高位 1 相同。

以 \(n=7\) 為例,我們將節點的值按最高位 1 分個組:\(\,\,\\)

注意到乙個事實:樹是二分圖。因為可以做二染色。以 \(n=7\) ,兩側分別為 \(3,4\) 個點為例

用 \(\,\\) 填充 \(3\) 個點的一邊,\(\\) 自然地對應另一邊

然後我們發現無論選哪個點對方都必敗

這個構造是通用的,因為分出來的組的大小是一堆 2 的整數次冪,而一定有一側的點不超過 \(\dfrac n2\) 個,可以通過二進位制拆分恰好填充

code

#includeconst int n=200010;

int t,n,cnt,x,y,m[2],a[n],b[n],last[n],v[2][n];

struct node e[n<<1];

void dfs(int x,int f,int c)

void fill(int x)

}

給定一棵 \(n(2\le n\le2\times10^5)\) 個點的帶權樹,一些邊的權值已經確定而其它的沒有確定(用 \(-1\) 表示)。

現在有 \(m(1\le m\le2\times10^5)\) 條資訊,表示 \(u_i\) 到 \(v_i\) 的路徑上的權值 xor 和的 popcount 為奇數還是偶數。

試構造出這樣一棵樹,或者輸出無解。

首先注意到 popcount ,對這東西來說每個二進位制位是等價的,所以我們可以嘗試把乙個數直接壓成一位 0/1

最直接的想法是進行二進位制分解,然後把每一位都 xor 起來。結果是 \((x)\bmod 2\) 。

簡單觀察一下,發現這步操作似乎非常正確,現在問題轉化為:

一棵樹,邊的權值為 0/1 ,一些邊權未知,給出 \(m\) 條路徑上的權值 xor 和,試構造一棵符合條件的樹。

當時想到這裡就不會了,然後寄了,rating-=114514

實際上只需考慮乙個簡單技巧:樹上差分/字首和。

隨便選個根節點 \(root\) ,令 \(a_i\) 表示 \(i\) 到 \(root\) 的路徑上的權值 xor 和。

那麼 \(u\) 到 \(v\) 的路徑上的權值 xor 和就是 \(a_u~~a_v\) 。

正確性顯然,\((u,v)\) 到 \(root\) 的路徑走了兩遍,貢獻自行抵消了。

是眾所周知的 trick 吧,,就我到現在才聽說 /youl

然後每條資訊給出的其實就是 \(a_u=a_v\) 和 \(a_u\ne a_v\) 中的乙個關係,已知權值的邊同理

並且 \(a_i\) 只有 0/1 兩種取值

寫個帶權並查集就沒了

code

#includeconst int n=200010;

int t,f,n,m,u[n],v[n],w[n],a[n],fa[n];

int find(int x)

int _union(int x,int y,int z)

int work()

while (m--)

for (int i=1; i<=n; ++i) find(i); //跑一遍路徑壓縮

return f;

}int main()

else puts("no");

return 0;

}

從數列 \(\\) 中選出一些數,使其中任意一對數 \(x,y\) 都滿足 \(x~~y\ge k\) 。

給出任意一種滿足要求且選出的數最多的方案,或輸出無解。

考慮 01 trie 。

顯然較高位不同的數相互獨立。這裡較高位指 \((k)\) 以上(不含)的二進位制位。

一組較高位相同的數中最多只能選出兩個。用 01 trie 判一下就好。

注意細節問題。

code

#include#include#include#include#define rep(i,l,r) for (int i=l; i<=r; ++i)

#define add(x) ans[++m]=x

const int n=300010;

int n,m,cnt,k,hb,x,y,f,a[n],ans[n],t[n<<5][2];

void ins(int x)

}int qry(int x)

int main()

rep(i,1,n) mp[a[i]=read()]=i;

std::sort(a+1,a+n+1);

hb=32-__builtin_clz(k); printf("%d\n",hb);

rep(i,1,n) if ((y=a[i]>>hb)==x)

else

if (cnt&&!f) add(a[n]);

if (m<=1) return puts("-1"),0;

printf("%d\n",m);

rep(i,1,m) printf("%d ",mp[ans[i]]);

return 0;

}

給乙個 \(n\times n\) 的網格,每個點上有乙個數 \(a_\)

定義 \(b_=a_~~a_~~a_~~a_\)

即上下左右四個相鄰數的異或和,如果在邊上就不算越界的數(或者認為外圍有一圈 0 )

給出 \(b\) ,求 \(a\) 中所有數的異或和。

神奇構造題,方法很多,但我乙個都沒想到 /kk

下面是一種方法

設 \(f_\) 表示 \(a_\) 參與異或的次數。我們只需在 \(b\) 中選取一些合適的元素異或起來,使所有 \(f_\) 都是奇數。

我們從左上到右下把網格掃一遍,如果當前 \(f_\) 為偶數,就選擇 \(b_\) ,並且更新它能影響到的四個 \(f\) 值

聽起來很暴力,我們似乎只保證了前 \(n-1\) 行的 \(f_\) 都是奇數,然而這個構造確實是對的

證明需要考慮乙個事:在第一行任意選擇一些 \(b_\) ,對於後 \(n-1\) 行都一定會有解。

來看這樣乙個選擇方案:

..x.....

.x.x....

x.x.x...

.x.x.x..

..x.x.x.

...x.x.x

....x.x.

.....x..

構造了乙個傾斜 \(45\) 度的x矩陣。

每個.都恰與偶數個x相鄰。

對於所有標x的位置,如果沒選 \(b_\) ,改為選;如果選了,改為不選。

對乙個可行解進行如上變換,一定會得到另乙個可行解,並且第一行特定位置的數的選擇情況改變(圖中為 \(b_\) )

題目保證一定有解,我們通過適當的變換,就可以使第一行處於任意選擇情況,並且仍有可行解。

總之意思就是第一行可以瞎選,反正後面能搞出解

然後我們發現第一行瞎選之後第二行的方案是確定的,因為 \(f_\) 為偶數時必須選 \(b_\) 才能補救,反之亦然

然後第二行也確定了,以此類推可以選完所有行

根據前面的證明,這種看似亂搞的選法就一定是可行解

**就非常簡單了

code

#include#define rev(x) x=(!x)

const int n=1010;

int t,n,ans,a[n][n]; bool f[n][n];

int main()

return 0;

}

異或 異或相關

感謝 morning glory 贊助 異或異 或 de scri ptio ndes crip tion 給定 l,r l,r,求 i lr j lr i ji l r j l r i jl,r 1 09l,r 1 09 s olut ions olut ion 假設l 1,r 4l 1,r 4,...

與異或操作相關的簡單演算法題

1 0 n n,n n 0 2 異或運算滿 換律和結合律 題目1 如何不使用額外變數交換兩個數int a 甲 int b 乙 a a b b a b a a b 把上面 分為三個步驟 第一步 a a b 此步驟過後 a 甲 乙 b 乙 第二步 b a b 此步驟過後 a a b 甲 乙 b a b ...

異或運算 有趣的異或運算

異或運算可以看做是沒有進製的加法,按位異或運算,相同為0,不同為1。0 0 0 0 1 1 1 0 1 1 1 0 觀察運算結果我們發現,當與0做異或運算時,另一元值不變 而與1做異或運算時,另一元值值取反。根據以上異或運算的特徵,可以有以下用途,除方便直觀外,運算效能也更加優異。1 變數重置0 假...