隐藏页面特效

OI算法竞赛常用套路及优化手段

 

 

经典的O3优化(一般写在开头)

 

#pragma GCC optimize("O3") #pragma G++ optimize("O3")

 

 

G++手动扩大栈(写在main的开始)

int size = 256 << 20; // 256MB char *p = (char*)malloc(size) + size; __asm__("movl %0, %%esp\n" :: "r"(p));

 

 

C++手动扩大栈(一般比较少起作用,推荐上面那个)

 

#pragma comment(linker, "/STACK:102400000,102400000")

 

G++ 64位手动扩大栈

extern int main2(void) __asm__ ("main2"); int main2(){ exit(0); }//在这里写main函数 int main(){ int size=64<<20; char *p=(char*)malloc(size)+size; __asm__ __volatile__("movq %0, %%rsp\n" "pushq $exit\n" "jmp main2\n" :: "r"(p)); }


读入优化

inline int rd(int& X){ char ch=X=0; for(;ch<'0' || ch>'9';ch=getchar()); for(;ch>='0'&&ch<='9';ch=getchar()) X=(X<<3)+(X<<1)+ch-'0'; return X; }

 

 

更快的读入优化

 

char str[10000001]; int stl=0; inline int rd(int& X){ char ch=X=0; for(;ch<'0' || ch>'9';ch=str[stl++]); for(;ch>='0'&&ch<='9';ch=str[stl++]) X=(X<<3)+(X<<1)+ch-'0'; return X; } int main(){ fread(str,10000000,1,stdin); }


如果输入文件较大以上方法可以把ch=str[stl++]改成自己实现的getchar,具体代码如下

 

 

#define SIZE 1000000 char str[SIZE]; int stl=SIZE; inline char getch(){ return stl==SIZE?fread(str,SIZE,1,stdin),str[stl=0]:str[stl++]; } inline int rd(int& X){ char ch=X=0; for(;ch<'0' || ch>'9';ch=getch()); for(;ch>='0'&&ch<='9';ch=getch()) X=(X<<3)+(X<<1)+ch-'0'; return X; }

 

 

线段树和树状数组记得使用位运算

ST表

不要用math库的log!自己预处理!

对于s[i][j]=f(s[i][j-1],s[i+(1<<j-1)][j-1]),我们将i,j的顺序调换一下可以提高效率

特别是在二维ST表效果显著,速度提升四倍有多

 

取模

首先不得不说把模数写成变量是不太好的做法。。。

我们可以

 

#define M 1000000007 const int M=1000000007

 

 

如果题目要求输入模数呢?

我们还是可以

 

int main(){ int p; scanf("%d",&p); const int Mod=p; }


如果只有加法,我们考虑判定性取模即可

 

 

int mod(int x){ return x>Mod?x-Mod:x; }

 

 

空间优化,记得C++有一个不常用的东西:short

在64位机器上少用指针因为它和long long一样费空间

 

对于一些奇怪的64位的OJ,我们可以使用__int128来过一些毒瘤数论题(比如对10^18取模)

 

关于memset和memcpy以及memmove

这几个函数效率都非常高,比循环的速度快很多

其实用法也很简单,比如原code是这样的

 

for(int i=l;i<=r;++i) a[i]=0; for(int i=l;i<=r;++i) a[i]=b[i]; for(int i=l;l<=r;++i) a[i]=b[i],b[i]=0;

我们可以改成

 

 

memset(a+l,0,r-l+1<<2); memcpy(a+l,b+l,r-l+1<<2); memmove(a+l,b+l,r-l+1<<2);


注意,左移的位数和a,b的类型有关,这里默认为int,sizeof(int)=4,所以就是左移2(乘4)

 

如果不知道a,b类型所占字节数,可以改成如下

 

memset(a+l,0,(r-l+1)*sizeof(a[0])); memcpy(a+l,b+l,(r-l+1)*sizeof(a[0])); memmove(a+l,b+l,(r-l+1)*sizeof(a[0]));

 

 

关于Dijkstra算法

一般的教材(误)上面都说:只适用于所有边权为正的情况

而实际上,dijk+heap是可以在负权图上面跑的,而且比spfa还好写

具体实现,去掉vis数组就好了,虽然这样效率可能比不上spfa了(因为有一个log)

 

关于负权回路和spfa

如果题目就是要你找一个负环,我们可以直接将queue<int>改成priority_queue<int>

这样通常效率会提高很多(仅限于找负环,跑一般的最短路会慢)

如果还想更快,就把priority_queue<int> 的比较函数变成以下的函数(自己实现一个类似greater<int>的东西)dist是距离

 

inline bool cmp(int a,int b){ return dist[a]>dist[b]; }

 

 

数据结构

  • 在维护树上路径时,如果只是点的独立的加减,可以考虑用括号序来维护(拆成两部分)
  • 需要求树上很多路径中k近/距离和 一类,考虑点分治/在点分树上解决。
  • 子树求和可以转化为DFS序上区间求和
  • 树状数组可以区间查询/修改(差分)
  • 需要查询序列上区间数据结构,只要满足总和是可以接受的范围,可以用线段树,每个区间维护一个这样的数据结构(例如AC自动机等)
  • 多维偏序问题,排序可以降维,CDQ分治可以降维,剩下只需要树状数组/线段树
  • 树上连通块有概率出现,再加上和的次方,往往可以拆开来,变成任意选K个可重,有序的点,考虑贡献。
  • 当又需要分块,每个块维护数据结构时,块的大小考虑调整(不再一定是n)(平衡规划)
  • 同理,对于图论中度数总和固定、多组询问查询的点数固定,查询点需要枚举出边,但可能一直查询度数比较大的点。此时考虑平衡规划。(询问点个数/度数 大于/小于n分开来做)
  • 对于点分治时两个不同的子树的结果混在一起需要判掉,可以考虑几种办法:
    • 在点分树上儿子记录当前点分树子树中的节点到父亲的结果,计算父亲时在这里减去。
    • 维护DFS序,两个子树对应两个无交的区间,可以考虑区间分裂一类的做法。
  • 对于有很多颜色的点,需要对相同颜色计算影响,可以把每个颜色拉出来在DFS序上搞事情(相邻+1,lca-1一类)
  • 如果又加上了深度限制,那么相当于除了DFS序这一维,还多出了深度这一维,可以考虑(主席树/CDQ分治/二维数据结构)
  • 对于这样一类问题:每个元素(边/点之类)具有权值/权值范围,每次只需要考虑权值是一定值/一定范围的元素的影响,可以考虑建立权值线段树,将元素的影响挂在线段树对应的所有区间上,查询就查询区间。
  • 当需要查询树上是否存在一条路径过两个点时,可以将路径端点记在DFS序上,然后两点子树查询,这就变成了两个区间数点的问题(二维偏序/扫描线/DFS动态树状数组维护增量)
  • 需要维护序列轮转问题时,不一定非要splay,如果轮转很特殊时可以采用线段树+预留空位的形式转化为单点修改。
  • 想要存储很多东西的0/1状态,且需要支持xor/or/and等操作时,bitset是个非常好的选择(计算复杂度可以除以32),别忘了bitset还有左移右移操作,可以用来处理+或-
  • 替罪羊树跑的很快。
  • 带旋转的平衡树是很难在内层套上线段树的,所以平衡树套线段树应考虑替罪羊树或treap
  • 一堆操作+询问的题,如果很容易处理一堆操作对一堆询问的贡献,可以考虑分治,你可以考虑权值/时间分治
  • 一棵Trie如果要维护+1异或,那么不妨从低位到高位建Trie
  • 线段树分治往往应用在一些对象知道插入和删除时间时,维护合法情况很容易,但撤销非法情况比较困难时。
  • 要算一个点和一堆点的距离的时候,可以考虑将距离拆成两点深度和-2*lca深度,lca深度可以表示成lca到根的节点数,那么直接树链剖分链上区间加区间求和即可。

图论

  • 求点双连通分量栈中仍然可以存点,圆方树维护起来很方便。
  • 无向图中最大值最小的路径一定在最小生成树上
  • 合并两个连通块的直径,直接比较四个端点两两连起来的长度即可。
  • 一些有代价的完美覆盖问题,选格子有行列限制有代价/收益的题往往考虑网络流。

多项式

  • 碰到诸如i=1n(1+pix)的时候,先不急着分治NTT,它既可以多项式ln+exp,又可以倍增。显然倍增更快。

其他

  • 如果遇到n3的转移矩阵,但是我们一次只想知道的结果是一维的(即暴力乘的复杂度是n2),那么可以考虑倍增预处理转移矩阵的幂,求出转移矩阵20,21,22...次的结果。询问的时候只需要n2log而不是n3log,预处理则是n3log,总的复杂度就可以变成qn2log+n3log
  • DP时,如果状态很大,结果很小,可以考虑能否将结果与状态互换。
  • 涉及网格图带权,行列选择限制/覆盖一类的问题,可以考虑网络流。
  • 一个经典问题:有一个序列,给出若干个区间,问有多少种选法使得选出的区间能覆盖整个序列。 我们考虑容斥,显然容斥系数是(-1)^强制不覆盖的位置个数,记f[i]为前i个位置都已经确定了,第i个位置不选的方案数和,它可以从f[j](1)2k转移而来,我们把所有区间挂在右端点,从左到右扫的时候做区间乘法即可。

 

援引

https://www.cnblogs.com/BAJimH/p/10569411.html

https://blog.csdn.net/JacaJava/article/details/78336840?utm_source=blogxgwz5


__EOF__

本文作者shenben
本文链接https://www.cnblogs.com/shenben/p/12769869.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   神犇(shenben)  阅读(1924)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
历史上的今天:
2017-04-24 [国家集训队2011]旅游(宋方睿)
点击右上角即可分享
微信分享提示