C++ STL的一些騷操作

priority_queue

最常用的當然是在dij的時候.

#include <queue>

struct node {
	int x, dis;
	bool operator < (const node a) const { return a.dis < dis; } 
	//需要注意的是這個地方要反着寫,一定要反着寫,也就是平常打cmp時的dis<a.dis改成a.dis<dis
	//然後在node a前面加上一個const
	
};

priority_queue<node> Q;

當然,這裏有很多種打法,我現在習慣於用下面這種,不用去記那些什麼const麻煩:

struct node {
	int x, dis;
	friend bool operator (node x, node y) { return x.dis < y.dis; }
}

priority_queue <node> Q;
  • Q.top().x就是每次堆頂的元素了。
Dijkstra這樣打:
priority_queue <node> Q; Q.push({i, 0}), mem(vis, 0), mem(dis, 7), dis[i] = 0;
while (Q.size()) {
	k = Q.top().x; Q.pop();
	if (vis[k]) continue; vis[k] = 1;
	for (x = las[k] ; x ; x = nex[x])
		if (dis[tov[x]] > dis[k] + 1)
			dis[tov[x]] = dis[k] + 1, Q.push({tov[x], dis[tov[x]]});
}

雖然priority_queue沒有set那麼好用,但畢竟set存不了實數,所以有時候優先隊列也是略勝一籌的。

它還有這些基本操作:

priority_queue<int> Q; //這樣就定義了一個優先隊列
priority_queue<double> Q;  //這樣也可以
priority_queue<node> Q; // 存儲一個結構體,例如像上面的dijkstra時的結構體。

//但如果不用結構體,我們可以這樣子打,在定義的時候確定運算符:
priority_queue<int, vector<int>, less<int> > Q; //這樣是從大到小的,也是C++默認的
priority_queue<int, vector<int>, greater<int> >Q; //這樣是從小到大的

注意 vector<int>以及後面的> >不要連起來.

和普通隊列一樣,它有這些基本操作:
q.size();//返回q裏元素個數
q.empty();//返回q是否爲空,空則返回1,否則返回0
q.push(k);//在q的末尾插入k
q.pop();//刪掉q的第一個元素

---這個操作最重要。。。。
q.top();//返回q的第一個元素

bitset

  • bitset用來壓位,使複雜度在原來的基礎上除以3232.
#include <bitset>

bitset<N> S; //這樣就可以建一個bitset了

S.set(); //全部變成1
S.reset(); //全部變成0

S.count(); // 返回1的個數
S.flip(); // 把S每一位都取反
S.flip(i); //僅把第i爲取反

S.any(); //返回是否有1
S.none(); //返回是否沒有1

///表示並不知道這個看似牛逼的操作有什麼用
a.to_ulong() //轉成一個unsigned long long的數.

//bitset支持位運算操作,非常強大.

如 a = a & b; a = a ^ b; a = a | b;

還需要注意的是,bitset支持左移、右移操作,得到一個新的bitset

具體的,這個操作可以用在一些形如fx=orfx+kmf_x=or_{f_{x+km}}的操作上,詳細見:https://jzoj.net/senior/#main/show/6123 這道題的DP優化。

vector

表示vector是個既能裝逼,又實用的東西,但請注意: vector常數巨大,一般如果是存邊的話,儘量用鄰接表或者前向星.

#include <vector>

vector <int> d,a[N]; //都可以定義

d.push_back(x) //在d這個vector裏面插入一個x元素,注意,相當於數組的插入.

printf("%d", d[i]) //詢問插入的第i個元素,注意下標從0開始
printf("%d", d.size() - 1) //返回插入元素個數
d.front(); d.back(); //第一個,最後一個元素,好吧實際上沒有用
d.clear(); //清空

把d排序:sort(d.begin(), d.end());
struct node {
	int u, dis;
	bool operaotr < (node x) const { return dis < x.dis; }
}
當然,也可以定義結構體vector<node>,然後按上面的一樣排序就可以了.

d.insert(d.begin() + k, v) // 表示在第k個位置插入一個元素v,把k+1~d.end()的都往後移一個位置.
//表示並不知道上面這個操作有什麼用,並且當k=0時,插入550000個元素,它用了49.007s
//插入100000個元素時,僅用了1.78s

int v = upper_bound(d.begin(), d.end(), node{a, b}) - d.begin() - 1;
//與普通的數組的upper_bound是類似的, 只不過有結構體時,需要operator.

注意,upper_bound是返回第一個大於某數的迭代器
lower_bound是返回第一個大於等於某數的迭代器,這兩個操作很重要,在set裏面也是一樣的.

總結一下,vector裏面這個upper_bound(a.begin(), a.end(), node{x,y}) - a.begin() - 1這個操作很重要.

然後就是vector雖然方便(比如說打點分樹的時候,需要把每一個分治中心帶的節點用結構體儲存,這時候就超級好用),但是也犧牲了時間,它的常數比較大.

map

map也是一個炒雞好用的STL。據cold_chair大佬所言,當元素很多的時候,map會變得很慢,或者說常數很大…

#include <map>

map<type1, type2> a; //這樣就定義了一個map.

需要注意的是,其中type1表示的是下標的類型,type2則表示的是存儲數的類型.

例如map<string, int> a, map<int, int> a...

修改、使用都是類似於:a[type1] = type2

a.erase(type1) // 表示刪除某個數
a.clear(); //清空
a.empty(); //判斷是否爲空
a.size(); //返回a的元素個數

map的用法還是很多的。

注意,一般來說,我們是定義

map <int, int> h;

如果要在某個位置賦值,可以直接調用

h[x] = y;

但注意,因爲幾乎所有的STL容器我們都只能用

:: iterator it;

來得到位置,所以如果我們要把對應的值求出來,則要用

it -> first 或 it -> seond

兩種操作。

最後還需要知道,map的實現是類似於set的,所以他也支持loglog的查找最小最大值。

具體來說,我們可以這樣調用:

for (map<int,int> :: iterator it = h.begin(); it != h.end(); it ++) {
    printf("%d\n",it->first)
    H[it->first] = 1;
}

Set

很強大的一個功能。

#include <set>

set <int> a; //這樣就建好一個set了
請務必注意:set是不可以支持重複元素的,想要有重複元素,可以使用multiset

multiset <int> a; //這是可以支持重複元素插入的。

a.insert(x); //表示在set裏面插入一個x元素.

a.erase(x); //表示把set裏面所有爲x的數的刪掉.
a.erase(a.find(x)); //這個表示只刪除一個x.

*a.begin() // 表示最小值
*a.end() // 表示最大值的下一個位置
*--a.end() //表示最大值

// 因爲返回的都是一個迭代器,所以具體數值要在前面加一個*號。

可能有時候要多次用到某個迭代器,所以我們可以把他儲存下來,用一個it來表示,其中it這樣定義:

multiset<int> :: iterator it;
//那麼像上面的最小值,就可以這樣表示it = a.begin(), printf("%d\n", *it).

it ++就可以表示下一個位置的元素,當然不要濫用,因爲這個++是需要log的時間複雜度的。。

其中,set裏面最重要的兩個操作就是上面vector裏面的兩個操作,即lower_bound和upper_bound.

it = a.lower_bound(x) //表示找到第一個大於等於x元素的迭代器
it = a.upper_bound(x) //表示找到第一個大於x元素的迭代器

注意,如果合併兩個map,我們可以用上面的loglog查找 + 對應位置賦值 實現。

但是set可以更方便的寫成:

Set1.insert(Set2.begin(),Set2.end());

這樣就把Set2插入到Set1中去了。

請注意,setsetmapmap都可以用來做啓發式合併,且常數幾乎一模一樣。

ctime

  • 測時間的以及隨機時候用的.
srand(time(0)) //隨機種子,沒有這個東西,隨機相當於沒有隨機.

主要想說的是這個:

int st = clock();

// do sth

printf("%lf\n", (double) (clock() - st) / CLOCKS_PER_SEC)

總結

  • wulala~終於把目前所學的一些知識整理了一下.

  • 鑑於自己學識淺薄,就不再多逼什麼了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章