題目大意
一棵n個點的樹,每個點有點權gi。
有q個詢問,每次詢問在點x到點y路徑上,選擇一些點,使得異或和最大,這個最大和為多少。
1≤n≤2104,1≤q≤2105,gi∈[0,260)
題目分析
對於小資料我們可以想到一種高斯消元解異或方程組的解法。從高位到低位,先假設當前位有1,然後在方程組中進行消元,如果沒有衝突就填1,有就填0,然後更新方程組。
但是滿分資料很大,這樣做是不行的。這裡我們就要用到一種叫做線性基的強大的東西。我們先來看看什麼叫線性基:
啊不~不是這個(這麼說我就是神犇了?哈哈哈哈哈~~~)。
線性基是什麼呢?就是給定乙個二進位制集合s,然後我們要求出乙個最小的集合s,這個集合內任意子集異或和能構成的數的集合,和原本二進位制集合任意子集異或和能構成的數的集合是一樣的。
說白了就是高斯消元解異或方程組最後剩下的矩陣。
換句話說利用線性基內的互相異或,就可以得出原集合互相異或的結果。如果二進位制位數為d,那麼線性基大小顯然不超過d。
可以證明,線性基內不存在異或和為0的子集。
我們採用d個d位二進位制數代表乙個線性基。第i位如果有數,那數一定是線性基裡面第d為1的最小的數。
那麼我們查詢最大異或和時可以直接從高位到低位貪心,如果異或上線性基第i位能使答案更大就異或,否則不異或。
線性基的合併就是和高斯消元差不多,對於第二個線性基的每乙個待插入數,我們列舉第乙個線性基里的位置d,如果該位置有數且當前待插入數d位為1,就把待插入數和該位異或,如果該位置沒有數且待插入數d位為1,就將待插入數插到這裡。
設二進位制有d位,那麼合併線性基的時間複雜度就是o(d2)。
回到這題,使用倍增維護樹上區間的線性基,暴力合併即可。
注意到這裡合併線性基的複雜度很大,如果合併多次是很難承受的。因此這裡我們要使用類rmq的查詢思想,分成四個區間(重疊部分不會影響答案),然後暴力合併。
總時間複雜度o(nlog2nd2+q(log2n+d2))。
**實現
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int read()
const int n=20050;
const int m=n<<1;
const int lgn=15;
const int d=60;
struct linear_basic
;typedef linear_basic lb;
lb operator+(lb x,lb y)
}return x;
}int last[n],high[n];
int tov[m],next[m];
int n,lgn,tot,q;
int fa[n][lgn];
lb f[n][lgn];
void insert(int x,int y)
void dfs(int x)
void pre()
int lca(int x,int y)
int p(int x,int h)
ll query(int x,int y)
int main()
}for (int i=1,x,y;i
SCOI2016 背單詞 題解
題意的話就看題面吧。我們一步一步的來分析 首先吃最少的泡椒,那麼顯然可以貪心,由於n n n times n n n貢獻的肯定比後面的方式都大,所以我們考慮將乙個串它的所有存在的字尾串全部先放在前面,這時就不會用第一種了,然後我們考慮,可以將這種關係用邊連起來,就成了一棵樹,我們可以舉個例子來看 5...
SCOI2016 幸運數字
線性基合併o log 2n 不能更小 但是倍增o qlog 3n 可過233333 甚至樹剖o qlog 4n 可過666666 還有乙個樹上路徑查詢利器 點分治!詢問離線 列舉重心,處理路徑過重心的詢問 邊dfs邊插入線性基,維護每個點到根路徑上的線性基 每個詢問,如果所屬不同的子樹,那麼過當前重...
SCOI2016 幸運數字
不想說了.就樹上的線性基合併.但是講道理o nlogn 3 為什麼能過去呢.但是就是能過去啊,因為博主是菜雞不怎麼會澱粉質啊,所以本篇題解只能提供這個複雜度的演算法了qaq 求選出來一些數使得異或和最大?線性基啊!那怎麼求路徑上的呢?乙個乙個往上合併,一直合併到lca就行了吧!乙個乙個合併顯然不行,...