板子哲学
不是说这里面的都是板子(
密码:#39C5BB
图论
数学
字符串
数据结构
搜索与递推
STL标准库
set 与 multiset
multiset
和set
的区别
1.set
内部元素不能重复,而 multiset
允许元素重复。
2.multiset
支持插入、删除和查找操作的平均时间复杂度均为 O(log n),而 set
只支持插入和查找操作的平均时间复杂度为 O(log n),删除操作的平均时间复杂度为 O(1)。
3.使用 multiset
时可以保存重复元素,但需要注意如果要删除某个元素,只能删除 multiset
中所有相同元素中任意一个,而不能只删其中的一些。
相同处:
都是有序排序且都是升序
通常情况下,如果不需要保存重复元素,优先使用 set
,因为 set
的查找操作更快;如果需要保存重复元素,或者需要使用 multiset
特有的功能(如保留相同元素),则使用 multiset
。
设 set<int>f[maxn];
.size()
表示节点i的集合大小(包括自己),可以用来找出父节点。
swap
(,)表示交换两个set容器
.lower_bound(key_value)
,返回中第一个大于等于key_value的迭代器
.erase(iterator)
,删除定位器iterator指向的值
.begin()
返回中第一个元素的迭代器
.end()
返回最后一个元素的迭代器
具体使用
//定义 set<int> s; set<QWQ> s; set<vector<int> >s; //vector中提供重载 < //结构体可正常重载运算符 //插入 O(log n) s.insert(i); //判断是否为空 O(1) s.empty(); //大小 O(1) s.size(); //清空 s.clear(); //值为x的元素个数 O(log n+ans) s.count(x); //迭代器(仅支持 ++ 与 -- ) // ++ 与 -- O(logn) //begin 与 end O(1) set<int>::iterator it=s.begin(); s.begin();//队首迭代器 s.end();//队尾迭代器,实际上--s.end才是指向集合中最大元素的迭代器(左闭右开) //查询 //在 set 中查找值为 x 的元素,并返回指向该元素的迭代器,若不存在,返回 set.end() O(log n) set.find(a); //删除 //参数可以是元素或者迭代器,返回下一个元素的迭代器 O(log n) set<int>::iterator it=s.begin(); s.erase(it); s.erase(3); //查找>=x的元素中最小的一个,并返回指向该元素的迭代器 s.lower_bound(x) //查找>x的元素中最小的一个,并返回指向该元素的迭代器 s.uppers.upper_bound(x) //若 x>最大元素 返回 s.end() //直接遍历就行 for(int i:s) cout<<i;
有关set的迭代器
/*`set<int>::iterator` 表示 `set<int>` 类型的迭代器, 用于遍历 `set` 中的元素。具体来说, 它是一个指向 `set<int>` 中元素的指针, 可以通过迭代器访问 `set<int>` 中的元素, 并能够进行迭代器运算,如自增等操作。 例如,对于一个 `set<int> s`,要遍历其中的元素, 可以使用 `set<int>::iterator it` 定义迭代器, 然后使用 `begin()` 和 `end()` 函数获取 `set` 中第一个元素和尾后元素的迭代器, 再通过循环遍历 `set` 中的所有元素,示例代码如下:*/ set<int>::iterator it; for (it = s.begin(); it != s.end(); ++it) { cout << *it << " "; } /*上述代码中,`it` 是 `set<int>` 的迭代器, `s.begin()` 和 `s.end()` 分别返回 `set<int>` 的第一个元素和尾后元素的迭代器, `*it` 则是获取当前迭代器指向的元素的值。*/ 针对题目中代码 for(int j:f[to]){ f[now].insert(j); } //----------------------------------// for(set<int>::iterator j=f[to].begin();j!=f[to].end();++j){ f[now].insert(*j); } 是等价的。
map
map
是一个关联式容器,其提供一对一的数据处理能力,类似于 hash表
,对于一个 first
元素仅指向一个 second
元素,某些题目可以让 first
作为数组下标来处理可能数组越界的需要桶的问题。
存在以下操作:
=迭代器=========
begin 返回指向容器起始位置的迭代器(iterator)
end 返回指向容器末尾位置的迭代器
cbegin 返回指向容器起始位置的常迭代器(const_iterator)
cend 返回指向容器末尾位置的常迭代器
=Capacity
size 返回有效元素个数
max_size 返回 unordered_map 支持的最大元素个数
empty 判断是否为空
=元素访问=
operator[] 访问元素
at 访问元素
=元素修改=
insert 插入元素
erase 删除元素
swap 交换内容
clear 清空内容
emplace 构造及插入一个元素
emplace_hint 按提示构造及插入一个元素
操作=========
find 通过给定主键查找元素,没找到:返回unordered_map::end
count 返回匹配给定主键的元素的个数
equal_range 返回值匹配给定搜索值的元素组成的范围
Buckets======
bucket_count 返回槽(Bucket)数
max_bucket_count 返回最大槽数
bucket_size 返回槽大小
bucket 返回元素所在槽的序号
load_factor 返回载入因子,即一个元素槽(Bucket)的最大元素数
max_load_factor 返回或设置最大载入因子
rehash 设置槽数
reserve 请求改变容器容量
而 map
基于红黑树实现,时间复杂度 ,unordered_map
则理论达到时间复杂度 ,但是带有常数。
一般来说,在我的测试下,应对阴间的极限数据时,unorder_map
表现更佳。
而对于 unordered_map
还可以进行我也不知道是什么东西的加速:
#include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/hash_policy.hpp> struct custom_hash{ static uint64_t splitmix64(uint64_t x){ x+=0x9e3779b97f4a7c15; x=(x^(x >> 30))*0xbf58476d1ce4e5b9; x=(x^(x >> 27))*0x94d049bb133111eb; return x^(x >> 31); } size_t operator()(uint64_t x) const{ static const uint64_t FIXED_RANDOM=std::chrono::steady_clock::now().time_since_epoch().count(); return splitmix64(x + FIXED_RANDOM); } }; __gnu_pbds::gp_hash_table<int,int,custom_hash> f;
在比较水的数据上并没有很大的体现,在 cf 的 1598F - RBS 题目上进行测试,共 124 个测试点,时间限制 3000ms,以下是对比:
-
map
卡在第 93 个测试点上,该测试点为极限数据,超时,内存 111500 KB。 -
unordered_map
用时 1169 ms,内存 112900 KB。 -
gp_hash_table
用时 779 ms,内存 110900 KB。
pair
可以把 pair
直接看做一个以第一元素为关键字升序排序的结构体。
(跑堆优化 dijistra
非常好用)
make_pair
就是把两个数构造一个 pair
。
bitset
std::bitset
可以看做一个二进制数,每 位占用一个字节,支持基本的位运算,可用于状态压缩, 位 bitset
执行单次运算的时间复杂度为 。
bitset
的主要优势在位运算极快,据说在 的情况下, 的算法 bitset
可过。
因为定义的时候就已经确定了位数所以操作时要求位数相同。
操作:
-
定义:
std::bitset<n> s(x)
表示定义一个 位的二进制数,数值上等于 。 -
s[k]
表示 的第 位(从右往左,编号从 开始),可以赋值或取值。 -
~s
对二进制数 取反。 -
&
、|
、^
返回两个位数相同的bitset
按位与、或、亦或运算结果。 -
<<
、>>
返回一个bitset
左移、右移若干位的结果(补 )。 -
==
、!=
比较两个位数相同的bitset
是否相等。 -
s.any()
当所有位都为 时返回false
,否则返回true
,s.none()
相反。 -
s.set()
将 的所有位变为 ,s.reset()
将 的所有位都变为 ,s.flip()
将 的所有位取反。
vector
比赛时申请双倍内存,谨慎使用。
杂项
(一些有意思但是没有用的东西?)
%:
可以代替 #
<% %>
可以代替 { }
有关评测环境
//注意,以下语句最多图个方便,不能在真正的比赛上使用。 #ifndef ONLINE_JUDGE freopen("T1.in","r",stdin); #endif //在评测环境下不执行如上freopen语句 //——————————————————————————————————————————————————————————// #if ONLINE_JUDGE char in[1<<20],*p1=in,*p2=in; #define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,1<<20,stdin),p1==p2)?EOF:*p1++) #endif //若是评测环境则开启fread //——————————————————————————————————————————————————————————// #if ONLINE_JUDGE #define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++) #else #define gh() getchar() #endif //在评测环境和其他环境的双定义
快读模板
//char in[1<<20],*p1=in,*p2=in; //#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,1<<20,stdin),p1==p2)?EOF:*p1++) //加上上两行是fread,1<<20是你读入的最大的可能的数的位数 //用fread调试时输出结果需要按crtl+z inline int read(){ char c=getchar(); int x=0,f=1; while(c<48)<%if(c=='-')f=-1;c=getchar();%> while(c>47)x=(x*10)+(c^48),c=getchar(); return x*f; }
快写模板
inline void write(int x){ if(x<0)<%putchar('-');x=~x+1;%> if(x>9) write(x/10); putchar(x%10+'0'); }
fread,fwrite
constexpr auto SIZE(1<<20); char in[SIZE],out[SIZE],*p1=in,*p2=in,*p3=out; #define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,SIZE,stdin),p1==p2)?EOF:*p1++) #define flush() (fwrite(out,1,p3-out,stdout)) #define putchar(x) (p3==out+SIZE&&(flush(),p3=out),*p3++=(x)) class Flush{public:~Flush(){flush();}}_; inline int read(){ int x(0);bool f(0);char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-'; for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); return f?x=-x:x; } inline void write(int x){ x<0?x=-x,putchar('-'):0;static short Sta[50],top(0); do{Sta[++top]=x%10;x/=10;}while(x); while(top) putchar(Sta[top--]|48); putchar('\n'); }
对拍模版
#include<bits/stdc++.h> #include<random> std::mt19937 ewq(time(nullptr)); inline int get(int l,int r){ //得到l-r区间的数 std::uniform_int_distribution<> QAQ (l,r); //<>不填写默认为int类型 // uniform_int_distribution生成整数,<>不填写默认为int //uniform_real_distribution生成实数 ,<>不填写默认为double return QAQ(ewq); } int main(){ freopen("in.txt","w",stdout); //输入文件 int n,m; n=get(1,831),m=get(1,39); //8月31是miku的生日哦 ,3月9是初音未来日哦 printf("%d %d\n",n,m); for(int i=1;i<=n;++i){ int x=get(114514,1919810); printf("%d\n",x); } return 0; } /* 用正确的暴力 freopen("in.txt","r",stdin); freopen("ans.txt","w",stdout); 得到正确答案之后 用认为的正解 freopen("in.txt","r",stdin); freopen("myout.txt","w",stdout); 与正确答案比对 */ //———————————————————————————而对拍也不一定要手动对拍,可以再开一个cpp来实现自动对拍,在 linux 环境下————————————————————————————// #include<bits/stdc++.h> #include<random> #include<ctime> int main(){ system("g++ T1++.cpp -o my -O2"); system("g++ T1f.cpp -o bl -O2"); system("g++ data.cpp -o mkd -O2"); //编译你的代码 int T=0; while(++T){ system("./mkd>T1.in"); double begin=clock(); system("./my<T1.in>my.out"); double end=clock(); system("./bl<T1.in>bl.out"); double t=(end-begin); // 运行 // “<in.in”表示从in.in读入数据 // ">my.out"从my.out输出数据 if(system("diff my.out bl.out")){ //判断是否正确 puts("Wrong Answer"); break; } else printf("Accepted√ %d\ntime=%.01lfms\n",T,t); if(t>1000){ printf("Time Limit Exceeded %.01lfms\n",t); break; } } return 0; }
火车头
//洛谷使用火车头会编译错误 %:pragma GCC optimize(3) %:pragma GCC optimize("Ofast") %:pragma GCC optimize("inline") %:pragma GCC optimize("-fgcse") %:pragma GCC optimize("-fgcse-lm") %:pragma GCC optimize("-fipa-sra") %:pragma GCC optimize("-ftree-pre") %:pragma GCC optimize("-ftree-vrp") %:pragma GCC optimize("-fpeephole2") %:pragma GCC optimize("-ffast-math") %:pragma GCC optimize("-fsched-spec") %:pragma GCC optimize("unroll-loops") %:pragma GCC optimize("-falign-jumps") %:pragma GCC optimize("-falign-loops") %:pragma GCC optimize("-falign-labels") %:pragma GCC optimize("-fdevirtualize") %:pragma GCC optimize("-fcaller-saves") %:pragma GCC optimize("-fcrossjumping") %:pragma GCC optimize("-fthread-jumps") %:pragma GCC optimize("-funroll-loops") %:pragma GCC optimize("-fwhole-program") %:pragma GCC optimize("-freorder-blocks") %:pragma GCC optimize("-fschedule-insns") %:pragma GCC optimize("inline-functions") %:pragma GCC optimize("-ftree-tail-merge") %:pragma GCC optimize("-fschedule-insns2") %:pragma GCC optimize("-fstrict-aliasing") %:pragma GCC optimize("-fstrict-overflow") %:pragma GCC optimize("-falign-functions") %:pragma GCC optimize("-fcse-skip-blocks") %:pragma GCC optimize("-fcse-follow-jumps") %:pragma GCC optimize("-fsched-interblock") %:pragma GCC optimize("-fpartial-inlining") %:pragma GCC optimize("no-stack-protector") %:pragma GCC optimize("-freorder-functions") %:pragma GCC optimize("-findirect-inlining") %:pragma GCC optimize("-fhoist-adjacent-loads") %:pragma GCC optimize("-frerun-cse-after-loop") %:pragma GCC optimize("inline-small-functions") %:pragma GCC optimize("-finline-small-functions") %:pragma GCC optimize("-ftree-switch-conversion") %:pragma GCC optimize("-foptimize-sibling-calls") %:pragma GCC optimize("-fexpensive-optimizations") %:pragma GCC optimize("-funsafe-loop-optimizations") %:pragma GCC optimize("inline-functions-called-once") %:pragma GCC optimize("-fdelete-null-pointer-checks") //在使用了快读的前提下,加在头文件前面,应对较强数据,可以优化 100ms+ //东西挺多的,有优化快读的,有手动开O3的
各类排序
-
插入排序 :把 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有 个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
-
冒泡排序 :单次将最大的元素往前冒泡。
-
归并排序 :分治,将两个的有序数列合并成一个有序数列。
-
快速排序 :分治,是冒泡排序的改进,通常时间复杂度低于 。
-
桶排序 :设置一个定量的数组当作空桶,遍历序列,并将元素一个个放到对应的桶中,对每个不是空的桶进行排序,从不是空的桶里把元素再放回原来的序列中。平均是 的(将值域平均分成 n 块 + 排序 + 重新合并元素)。
-
计数排序 ( 指值域最大值):计算每个数出现了几次,求出每个数出现次数的前缀和,利用出现次数的前缀和,从右至左计算每个数的排名。
-
基数排序 ( 指进制下最大位数),详见下阅读程序 17.
-
希尔排序 :这是一个不稳定的排序,时间复杂度通常为 。
-
堆排序 :这是一个不稳定的排序,首先建立大顶堆,然后将堆顶的元素取出,作为最大值,与数组尾部的元素交换,并维持残余堆的性质,之后将堆顶的元素取出,作为次大值,与数组倒数第二位元素交换,并维持残余堆的性质,以此类推,在第 次操作后,整个数组就完成了排序。
查理线段树
如题,将 id<<1
改为 mid<<1
,将 id<<1|1
改为 mid<<1|1
即可,可以将线段树的空间复杂度可以从 降为 。
证明:
评测对比:题目:[JOISC2022] 监狱 (本题需要线段树优化建图因此建了两棵线段树,且测试点共 个全部通过足以证明查理线段树的正确性)
于是你发现它没什么用(起码在动态内存评测的情况下,是这样的)
看个乐呵得了。
assert 函数
assert(表达式)
是 assert 函数的基本用法,需调用 <assert.h>
库。
其主要的作用就是如果括号内表达式成立,停止程序运行。
Sonnety自用封装九九新
#include<bits/stdc++.h> using namespace std; #define il inline #define rg register int typedef long double llf; typedef long long ll; typedef pair<int,int> PII; const double eps=1e-8; namespace io{ #if ONLINE_JUDGE char in[1<<20],*p1=in,*p2=in; #define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,1<<20,stdin),p1==p2)?EOF:*p1++) #endif //若是评测环境则开启fread il int read(){ char c=getchar(); int x=0,f=1; while(c<48)<%if(c=='-')f=-1;c=getchar();%> while(c>47)x=(x*10)+(c^48),c=getchar(); return x*f; } //快读 il void write(int x){ if(x<0)<%putchar('-');x=~x+1;%> if(x>9) write(x/10); putchar(x%10+'0'); } //快写 il int ins(char *str){ int len=0; while(1){ char c=getchar(); if(c!='\n' && c!='\0' && c!='\r') str[++len]=c; else{ //if(len==0) continue; 有些数据可能因为造的数据有问题而最后出现重复空格或换行 break; } } return len; } //getchar快速读入字符串并返回字符串长度,下标从1开始 } namespace mystd{ il int Max(int a,int b)<%if(a<b) return b;return a; %> il int Min(int a,int b)<%if(a>b) return b;return a; %> il int Abs(int a)<% if(a<0) return a*(-1);return a; %> //整数 il double fMax(double a,double b)<%if(a<b) return b;return a; %> il double fMin(double a,double b)<%if(a>b) return b;return a; %> il double fAbs(double a)<% if(a<0) return a*(-1);return a; %> //浮点数 il int dcmp(double a){ if(a<-eps) return -1; if(a>eps) return 1; return 0; } //精度处理 } namespace mathstd{ const int mod=1e9+7; int gcd(int x,int y){ if(y==0) return x; else return gcd(y,x%y); } int lcm(int x,int y)<% return (x/gcd(x,y))*y; %> //辗转相除最大公约数or最小公倍数 il ll qpow(ll x,int y){ ll res=1; while(y){ if(y&1) res=res*x%mod; x=x*x%mod; y=y>>1; } return res; } //快速幂求x^y il ll getinv(ll x)<% return qpow(x,mod-2); %> //费马小定理求逆元 int x,y; void exgcd(int a,int b){ if(b==0)<% x=1;y=0; %> else{ exgcd(b,a%b); int save=x; x=y,y=save-(a/b)*y; } } //拓展欧几里得求a在模b意义下的逆元x const int maxn=1e7+50; int fac[maxn],inv[maxn],facinv[maxn]; il void pre_facinv(int w){ fac[0]=fac[1]=1; inv[0]=inv[1]=1; facinv[0]=facinv[1]=1; for(rg i=2;i<=w;++i)<% fac[i]=i*fac[i-1]%mod; %> for(rg i=2;i<=w;++i)<% inv[i]=(mod-mod/i*inv[mod%i]%mod+mod)%mod; %> for(rg i=2;i<=w;++i)<% facinv[i]=facinv[i-1]*inv[i]%mod; %> } //线性求阶乘逆元 il ll getc(ll y,ll x){ if(x<y) return 0; return fac[x]%mod*facinv[y]%mod*facinv[x-y]%mod; } ll lucas(ll y,ll x){ if(y==0) return 1; return getc(y%mod,x%mod)%mod*lucas(y/mod,x/mod)%mod; } //lucas定理求组合数C(x,y) int n; int CRT(int m[],int r[]){ int mod=1,ans=0; for(int i=1;i<=n;++i){ mod*=m[i]; } for(int i=1;i<=n;++i){ int c=mod/m[i],x=0,y=0; exgcd(c,m[i]); ans=(ans+r[i]*c*x%mod)%mod; } return (ans%mod+mod)%mod; } //中国剩余定理求解互质的互余方程组 } struct Node<% double x,y; Node(double xx=0,double yy=0)<% x=xx,y=yy; %> %>; il bool operator==(Node a,Node b)<% return !mystd::dcmp(a.x-b.x) && !mystd::dcmp(a.y-b.y); %> il Node operator+(Node a,Node b)<% return Node(a.x+b.x,a.y+b.y); %> il Node operator-(Node a,Node b)<% return Node(a.x-b.x,a.y-b.y); %> il Node operator*(Node a,double k)<% return Node(a.x*k,a.y*k); %> namespace Vector{ il double dot(Node a,Node b)<% return a.x*b.x+a.y*b.y; %> //点乘 il double len(Node a)<% return sqrt(dot(a,a)); %> //模长 il double angle(Node a,Node b)<% return acos(dot(a,b)/len(a)/len(b)); %>//夹角 il double cro(Node a,Node b)<% return a.x*b.y-a.y*b.x; %> //叉积 il int judge_LINE(Node p,Node a,Node b)<% return !mystd::dcmp(cro(p-a,b-a)); %> //判断p是否在直线AB上 il int judge_line(Node p,Node a,Node b)<% return !mystd::dcmp(cro(p-a,b-a)) && mystd::dcmp(mystd::fMin(a.x,b.x)-p.x)<=0 && mystd::dcmp(p.y-mystd::fMax(a.y,b.y))<=0 && mystd::dcmp(p.x-mystd::fMax(a.x,b.x))<=0 && mystd::dcmp(mystd::fMin(a.y,b.y)-p.y)<=0; %> //判断p是否在线段AB上 il Node footnode(Node p,Node a,Node b){ Node x=p-a,y=p-b,z=b-a; double len1=dot(x,z)/len(z),len2=-1.0*dot(y,z)/len(z); return a+z*(len1/(len1+len2)); } //求p到直线AB的垂足 il Node symmetry(Node p,Node a,Node b)<% return p+(footnode(p,a,b)-p)*2; %> //求p到直线AB的对称点 il Node internode(Node a,Node b,Node c,Node d)<% Node x=b-a,y=d-c,z=a-c;return a+x*(cro(y,z)/cro(x,y)); %> //直线AB与CD交点 il int judge_CROSS(Node a,Node b,Node c,Node d)<% return judge_line(internode(a,b,c,d),c,d); %> //判断直线AB与线段CD是否有交点 il int judge_cross(Node a,Node b,Node c,Node d){ double c1=cro(b-a,c-a),c2=cro(b-a,d-a); double d1=cro(d-c,a-c),d2=cro(d-c,b-c); return mystd::dcmp(c1)*mystd::dcmp(c2)<0 && mystd::dcmp(d1)*mystd::dcmp(d2)<0; } //判断线段AB与线段CD是否有交点 il int PIP(Node *P,int n,Node a){ int cnt=0,j; double res=0.0; for(rg i=1;i<=n;++i){ if(i<n) j=i+1; else j=1; if(judge_line(a,P[i],P[j])) return 2;//点在多边形上 if(a.y>=mystd::fMin(P[i].y,P[j].y) && a.y<mystd::fMax(P[i].y,P[j].y)){ //纵坐标在该线段两端点之间 res=P[i].x+(a.y-P[i].y)/(P[j].y-P[i].y)*(P[j].x-P[i].x); //res是同一纵坐标下的在该边上的点的横坐标 cnt=cnt+(mystd::dcmp(res-a.x)>0); } } return cnt&1; //奇数次就是在多边形内 } //判断点a是否在多边形P内部 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现