pbds 库及优化

https://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/
开启 pb_ds 库:

#include<bits/extc++.h>
using namespace __gnu_pbds;

注意重名问题:__gnu_pbds::priority_queue<int> q;

priority_queue

参数:
typename value_type

typename cmp:默认 less<(value type)>,可以自定义

typename tag: pairing_heap_tag。

用法:
point_iterator push(const_reference)

void modify(point_iterator, const_reference)

void erase(point_iterator)

priority_queue<int> p;
priority_queue<int>::point_itreator it = p.push(0);
p.push(1); p.push(2); //p.top() == 2
p.modify(it, 3); //p.top() == 3
p.erase(it); //p.top() == 2

void join(priority_queue &other)
合并两个堆,把 other 合并到 *this,然后 other 会被清空。

(白送一个可并堆)
一共五个操作,push, pop, modify, erase, join。一个询问,top。

优化 dijkstra 算法,使用 pairing_heap_tag。和手写数据结构一样快。

实际测试

测试:
给定 \(10^7\) 个数,插入堆,然后一个个 pop 出来。

  • std::priority_queue 1.08s
  • pairing_heap_tag 0.92s
  • binomial_heap_tag 1.31s
  • thin_heap_tag 2.38s

好吧,全都用 pairing 就好啦!

tree

参数:
typename key

typename mapped

typename cmp_fn = std::less<...>

typename tag = rb_tree_tag/splay_tree_tag/ov_tree_tag

class Node_Update = null_tree_node_update, 可以使用 tree_order_statistics_node_update,这样就可以获得两个函数 find_by_order 和 order_of_key!(树_顺序_统计数据_节点_上传)

用法:

iterator find_by_order(size_type order)

找第 order + 1 小的元素的迭代器,如果 order 太大会返回 end()

(注意是 order + 1,也就是 0-indexed 的 order!)

size_type order_of_key(const_key_reference r_key)

询问 tree 里面有多少个比 r_key 小的元素(也就是最小的 0-indexed rank)

void join(tree &other)

把 order 的所有元素移动到 *this 中,要求两个树的值域不能相交,否则会抛出异常。(合并操作)

void split(const_key_reference r_key, tree &other)

(分裂操作)清空 other, 然后把 *this 中所有大于 r_key 的元素移动到 other

自定义 node_update:不会,不会。

一共四个操作,insert, erase, join, split,三个询问,find, find_by_order, order_of_key。
(最后两个,order 为基础,记得是 0-indexed,查询 kth 是 find_by_order,查询 rnk 是 order_of key)

记得 tree_order_statistics_node_update,再怎么样也要记住第一个单词是 tree。

依然,只使用 rb_tree_tag。

hash_table

gp_hash_table<key, mapped>

支持 find 和 []

Pjudge NOIP Round #4

【题意】
虱子国王尼特这天有点不舒服,它周围的 \(n\) 个医生立刻开出了药方:第 \(i\) 个医生告诉它,从这天起的第 \(L_i\) 天到第 \(R_i\) 天,它应该服用 \(x_{i,1},x_{i,2},…,x_{i,K_i}\)\(K_i\) 种药,每天每种药应当服用恰好一片。注意,如果有多个医生的药方里都要求尼特在第 \(p\) 天服用第 \(q\) 种药,那尼特在第 \(p\) 天仍然只会服用一片第 \(q\) 种药。编号为 \(j\) 的药每片需要 \(c_j\) 元钱。

然而,由于尼特的疏忽,有恰好一位庸医混进了医生队伍里,但尼特并不知道哪位医生是庸医。所以它想知道,对于所有 \(1≤i≤n\),如果它按照除了第 \(i\) 个医生之外的所有医生的药方吃药,它总共将花费多少钱。

\(n,m \le 5\times 10^5,\sum K_i \le 10^6, c_i \le 10^6\)

【分析】
按照上次那道区间颜色的套路,我们首先求出所有医生的话都要听的时候的答案,然后对于每个医生考虑消除贡献。

“首先”这一步怎么做?对每一个颜色维护区间并,然后乘以权值即可。这部分代码:

f(i,1,m) {
        int sum = 0, l = 0, r = 0;
        sort(y[i].begin(),y[i].end(),cmp);
        for(pair<pii,int> j : y[i]) {
            if(j.fi.fi <= r) r = max(j.fi.se,r);
            else {sum+=(r==0?0:dis[r]-dis[l]); l=j.fi.fi,r=j.fi.se; }
        }
        sum+=(dis[r]-dis[l]);
        pans+=sum*c[i];
}

然后考虑维护一个颜色里有哪些是只有一个医生覆盖的区间。考虑扫描线并且维护一个 set,表示当前扫到的点里面有哪些医生。如果只有一个医生,那么整个区间都应该算贡献。这要怎么做比较好写?如果直接用 \(l\)\(r\) 处理,会遇到一些边界情况。其实考虑 \(r + 1\) 时刻,set 中才会真正删去一个点。这样把 \(r\)\(1\) 再处理会更好。

时间复杂度 \(O(n \log n)\)。这次还真过不去。

考虑卡常。把 set 换成 gp_hash_table。然后构造函数记得算时间复杂度,不要退化了。

然后过了。

#include<bits/stdc++.h>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
int dis[1000010], c[1000010], ans[1000010]; 
int n,m; int v;
struct doc{ 
    int l,r,k;
    vector<int> p;
}d[1000010];
int enc(int x) {return lower_bound(dis+1,dis+v+1,x)-dis;}
#define fi first 
#define se second
bool cmp(pair<pii,int> x,pair<pii,int> y){
    if(x.fi.fi!=y.fi.fi)return (x.fi.fi<y.fi.fi);
    return (x.fi.se<y.fi.se);
}
vector<pair<pii,int>>y[1000010];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    cin>>n>>m;
    f(i,1,m)cin>>c[i];
    int cnt=0;
    f(i,1,n){
        cin>>d[i].l>>d[i].r>>d[i].k;
        d[i].r++;
        dis[++cnt]=d[i].l; dis[++cnt]=d[i].r;
        f(j,1,d[i].k) { int x; cin>>x; d[i].p.push_back(x); }
    }
    sort(dis+1,dis+cnt+1); v=unique(dis+1,dis+cnt+1)-dis-1;
    int pans = 0;
    f(i,1,n) for(int j : d[i].p) y[j].push_back({{enc(d[i].l),enc(d[i].r)},i});
    f(i,1,m) {
        int sum = 0, l = 0, r = 0;
        sort(y[i].begin(),y[i].end(),cmp);
        for(pair<pii,int> j : y[i]) {
            if(j.fi.fi <= r) r = max(j.fi.se,r);
            else {sum+=(r==0?0:dis[r]-dis[l]); l=j.fi.fi,r=j.fi.se; }
        }
        sum+=(dis[r]-dis[l]);
        pans+=sum*c[i];
	}
    f(i,1,n)ans[i]=pans;   
	int ccc = 0;
	gp_hash_table<int, null_type> stk;
	vector<vector<int>> jin(cnt+1); vector<vector<int>> chu(cnt+1);vector<int> vis;
    f(i,1,m){
        stk.clear(); vis.clear();
        for(pair<pii,int> j : y[i]) {
            vis.push_back(j.fi.fi); vis.push_back(j.fi.se);
        }
        for(int i : vis) {
        	jin[i].clear(); chu[i].clear();
		}
		for(pair<pii,int> j : y[i]) {
            jin[j.fi.fi].push_back(j.se); chu[j.fi.se].push_back(j.se); 
        } 
        sort(vis.begin(),vis.end());
        int ccnt = unique(vis.begin(),vis.end())-vis.begin()-1; 
		int lst = 0;  
        f(jj,0,ccnt){
        	ccc++; 
			int j=vis[jj];
            if(!chu[j].size()&&!jin[j].size())continue;
            if(stk.size() == 1) { 
				int dx = (*(stk.begin())); ans[dx]-=c[i]*max(0ll, dis[j]-1-lst + 1);
            }
            for(int k : jin[j]) { stk.insert(k); if(stk.size() == 1) lst = dis[j]; }
            for(int k : chu[j]) { stk.erase(k); if(stk.size() == 1) lst = dis[j]; }
        }
    }
    f(i,1,n)cout<<ans[i]<<" "; cout <<endl;
	time_t finish = clock();
   // cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}

卡哈希以及防止卡哈希

https://www.luogu.com.cn/blog/221955/guan-yu-hack-unorderedset

posted @ 2022-11-22 11:13  OIer某罗  阅读(197)  评论(0编辑  收藏  举报