連續多場 cf 都卡在 xor 題,並且以各不相同的方式寄了。不得不惡補一下這方面的處理技巧
codeforces 訪問有點慢,所以掛洛谷的題目鏈結
e 和 s 在玩博弈遊戲, e 先手 s 後手。\(u~~v\le \min(u,v)\) 顯然需要處理一下。容易發現它等價於 \(u\) 和 \(v\) 的最高位 1 相同。給一棵 \(n\) 個節點的樹,節點的值包含 \([1,n]\) 中的每乙個值。
e 先隨便選擇乙個點,占領它(點 \(u\) ), s 只能選擇與這個點相鄰的,沒有被占領的點(點 \(v\) )且這兩個點滿足 \(u⊕v≤\min(u,v)\)
\(⊕\) 是異或操作
現在把樹給 e 了, e 想要重新排列節點的值(樹的形狀不變,只是調換結點的值)來達到這個目的:
最大化第一輪能夠選擇的點的數量,在選了這個點之後,e 必贏。
贏:對手無路可走,你就算贏
以 \(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\) 表示)。首先注意到 popcount ,對這東西來說每個二進位制位是等價的,所以我們可以嘗試把乙個數直接壓成一位 0/1現在有 \(m(1\le m\le2\times10^5)\) 條資訊,表示 \(u_i\) 到 \(v_i\) 的路徑上的權值 xor 和的 popcount 為奇數還是偶數。
試構造出這樣一棵樹,或者輸出無解。
最直接的想法是進行二進位制分解,然後把每一位都 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_\)神奇構造題,方法很多,但我乙個都沒想到 /kk定義 \(b_=a_~~a_~~a_~~a_\)
即上下左右四個相鄰數的異或和,如果在邊上就不算越界的數(或者認為外圍有一圈 0 )
給出 \(b\) ,求 \(a\) 中所有數的異或和。
下面是一種方法
設 \(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 假...