SCOI2016 JZOJ4632 幸運數字

2021-12-29 20:45:50 字數 1785 閱讀 3341

題目大意

一棵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就行了吧!乙個乙個合併顯然不行,...