靶機Kioptrix: Level 1.3 (#4)攻略
阿新 • • 發佈:2022-04-06
例題:bzoj4919大根堆
洛谷P4577領導集團問題
以大根堆為例
就是說我在將它和它的子樹合併的時候
如果他的子樹裡有比它大的,那麼我直接將
大於等於它的第一個數換進去
這樣能保證在序列長度不變的情況下使最大值最小,如果它子樹裡沒有比他大的,直接插入就行
eg : 1 4 7 8 9
我插入6,那麼就會把7換掉,這樣,序列長度還是5不會變,但是如果單獨拿出來看146肯定是比147好的(啟發式合併,將小的向大的合併,所以將最大的根越小越好->更容易合併進大的)但如果用146合併出來的比序列長為5的短,那麼對答案不會有影響
但如果比5長,那麼以6插入肯定比7好
大根堆
點選檢視程式碼
#include <bits/stdc++.h> #define LL long long #define Re register int #define ki cout << endl #define LD double using namespace std; namespace kiritokazuto { template <typename T> inline void in(T &x) { int f = 0; x = 0; char c = getchar(); while(c < '0' || c > '9')f |= c == '-', c = getchar(); while(c >= '0' && c <= '9')x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); x = f ? -x : x; } template <typename T> inline void ot(T x) { if(x < 0)putchar('-'), x = -x; if(x > 9)ot(x / 10); putchar(x % 10 | '0'); } } const int maxn = 2e5 + 100; const int Inf = 1e9; using namespace kiritokazuto; //就是想試試 這個 “啟發式合併 ” //在當前大根堆大小相同的時候,頂點的權值越小,就能合併給更大的大根堆 //啟發式合併 -> 小的往大的裡和 -> 所以不去考慮當前頂點的權值更大 -> 這樣是將別的合併到當前 -> 就不是啟發了(。。。好像倒過來也是?) /* 當前點與子樹合併 如果子樹內有比當前點更大的值 答案不會變 直接替換set中比它大與它相鄰的樹 否則直接加進來 */ /* set c 產生一個空的set/multiset,不含任何元素 set c(op) 以op為排序準則,產生一個空的set/multiset //strcut cmp { bool operator() (const rec &a, const rec&b) { return a.x < b.x || a.x == b.x && a.y < b.y; } } ; multiset<rec, cmp> mul; // set c1(c2) 產生某個set/multiset的副本,所有元素都被拷貝 set c(beg,end) 以區間[beg,end)內的所有元素產生一個set/multiset set c(beg,end, op) 以op為排序準則,區間[beg,end)內的元素產生一個set/multiset c.~set() 銷燬所有元素,釋放記憶體 set<Elem> 產生一個set,以(operator <)為排序準則 set<Elem,op> 產生一個set,以op為排序準則 count (elem) 返回元素值為elem的個數 find(elem) 返回元素值為elem的第一個元素,如果沒有返回end() lower _bound(elem) 返回元素值為elem的第一個可安插位置,也就是元素值 >= elem的第一個元素位置 upper _bound (elem) 返回元素值為elem的最後一個可安插位置,也就是元素值 > elem 的第一個元素位置 equal_range (elem) 返回elem可安插的第一個位置和最後一個位置,也就是元素值==elem的區間 c.size() 返回當前的元素數量 c.empty () 判斷大小是否為零,等同於0 == size(),效率更高 c.max_size() 返回能容納的元素最大數量 c1 == c2 判斷c1是否等於c2 c1 != c2 判斷c1是否不等於c2(等同於!(c1==c2)) c1 < c2 判斷c1是否小於c2 c1 > c2 判斷c1是否大於c2 c1 <= c2 判斷c1是否小於等於c2(等同於!(c2<c1)) c1 >= c2 判斷c1是否大於等於c2 (等同於!(c1<c2)) c1 = c2 將c2的元素全部給c1 c1.swap(c2) 將c1和c2 的元素互換 swap(c1,c2) 同上,全域性函式 c.begin() 返回一個隨機存取迭代器,指向第一個元素 c.end() 返回一個隨機存取迭代器,指向最後一個元素的下一個位置 c.rbegin() 返回一個逆向迭代器,指向逆向迭代的第一個元素 c.rend() 返回一個逆向迭代器,指向逆向迭代的最後一個元素的下一個位置 c.insert(elem) 插入一個elem副本,返回新元素位置,無論插入成功與否。 c.insert(pos, elem) 安插一個elem元素副本,返回新元素位置,pos為收索起點,提升插入速度。 c.insert(beg,end) 將區間[beg,end)所有的元素安插到c,無返回值。 c.erase(elem) 刪除與elem相等的所有元素,返回被移除的元素個數。 c.erase(pos) 移除迭代器pos所指位置元素,無返回值。 c.erase(beg,end) 移除區間[beg,end)所有元素,無返回值。 c.clear() 移除所有元素,將容器清空 sets和multisets的迭代器是雙向迭代器 對迭代器操作而言 所有的元素都被視為常數 可以確保你不會人為改變元素值 從而打亂既定順序 所以無法呼叫變動性演算法,如remove()。 必須保證引數有效 迭代器必須指向有效位置 序列起點不能位於終點之後 不能從空容器刪除元素。 */ #define mulit multiset <int> :: iterator #define si(x) mul[x].size() multiset <int> mul[maxn];//存最長序列的尾部 vector <int> G[maxn]; int n, val[maxn]; void dfs(int u) { //如果當前子樹沒有更大的,直接加,就是當前的根節點嚴格大於尾部 //否則,當前跟節點小於等於尾部, 則替換大於等於它的第一個數(lower_bound),保證長度最長且權值最小 for(Re i = 0, to; i < G[u].size(); i ++) { to = G[u][i]; // if(to == fa)continue; dfs(to); if(si(to) > si(u))swap(mul[to], mul[u]); for(mulit it = mul[to].begin(); it != mul[to].end(); it++) mul[u].insert(*it);//mulit預設從小到大 mul[to].clear(); } mulit it = mul[u].lower_bound(val[u]); if(it != mul[u].end()) mul[u].erase(it); mul[u].insert(val[u]); } signed main () { in(n); for(Re i = 1, x; i <= n; i ++) { in(val[i]); in(x); //G[i].push_back(x); G[x].push_back(i); } dfs(1); ot(mul[1].size()); return 0; }
領導集團問題
點選檢視程式碼
#include <bits/stdc++.h> #define LL long long #define Re register int #define ki cout << endl #define LD double using namespace std; namespace kiritokazuto { template <typename T> inline void in(T &x) { int f = 0; x = 0; char c = getchar(); while(c < '0' || c > '9')f |= c == '-', c = getchar(); while(c >= '0' && c <= '9')x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); x = f ? -x : x; } template <typename T> inline void ot(T x) { if(x < 0)putchar('-'), x = -x; if(x > 9)ot(x / 10); putchar(x % 10 | '0'); } } const int maxn = 2e5 + 100; const int Inf = 1e9; using namespace kiritokazuto; #define mulit multiset <int> :: iterator #define si(x) mul[x].size() multiset <int> mul[maxn];//存最長序列的尾部 vector <int> G[maxn]; int n, val[maxn]; void dfs(int u) { for(Re i = 0, to; i < G[u].size(); i ++) { to = G[u][i]; dfs(to); if(si(to) > si(u))swap(mul[to], mul[u]); for(mulit it = mul[to].begin(); it != mul[to].end(); it++) mul[u].insert(*it);//mulit預設從小到大 // mul[to].clear(); } mul[u].insert(val[u]);//先插入,保證有... mulit it = mul[u].lower_bound(val[u]); if(it != mul[u].begin()) mul[u].erase(--it);//這次該改begin } signed main () { // freopen("init.in.txt", "r", stdin); // freopen("outt.out.txt", "w", stdout); in(n); for(Re i = 1, x; i <= n; i ++) { in(val[i]); } for(Re i = 2, x; i <= n; i ++) { in(x); G[x].push_back(i); } dfs(1); ot(mul[1].size()); // cout << "??"; return 0; }