ACM选手进阶指北:一个好的代码库与latex维护代码文档
一个好的代码库是必须的,打的久的人心里自然有b数
而且对于算法模板而言,简单的文件夹分级维护就能满足所有需求了
当然,好的代码库不只是把代码堆进去那么简单,
还需要随用随取,变量名不冲突,风格一致,封装优秀等等
并且每次写题都用自己的板子,不断精进细节
非常推荐使用封装,默认参数,宏定义,有意义的变量名,统一取名习惯和常数名等等
例如主席树的模板代码,直接复制粘贴就能用,也不会和你写了一半的其他代码冲突:
int rt[maxn];//@树根@ class ptree{public: #define nd node[now] #define ndp node[pre] #define mid (s+t)/2 int cnt; struct segnode{int l,r,sum;}node[maxn*30]; void maketree(int s,int t,int &now=rt[0]){ now=++cnt;nd={s,t,0}; if(s==t) return ; maketree(s,mid,nd.l); maketree(mid+1,t,nd.r); } void update(int pos,int val,int s,int t,int &now,int pre){ now=++cnt;nd=ndp;nd.sum+=val; if(s==t) return ; if(pos<=mid) update(pos,val,s,mid,nd.l,ndp.l); else update(pos,val,mid+1,t,nd.r,ndp.r); } ll query(int l,int r,int s,int t,int now,int pre){ if(l<=s&&r>=t) return nd.sum-ndp.sum; ll sum=0; if(l<=mid) sum+=query(l,r,s,mid,nd.l,ndp.l); if(r>mid) sum+=query(l,r,mid+1,t,nd.r,ndp.r); return sum; } #undef mid }tree;
再比如重链剖分的模板,用了默认参数,也写了一点注释,用的时候只需要改update和query就行了:
class graph{public://@按点@ struct node{int to,next;}e[maxn<<1]; int head[maxn],nume,mp[maxn]; inline void add(int a,int b){ e[++nume]={b,head[a]}; head[a]=nume; } int ltop[maxn],fa[maxn],deep[maxn]; int sz[maxn],remp[maxn]; int son[maxn],cnt; void init(int n){rep(i,1,n) head[i]=0;cnt=0,nume=1;} void dfs1(int now=1,int pre=1,int d=0){ deep[now]=d,fa[now]=pre,sz[now]=1,son[now]=0; forn(i,now){ int to=e[i].to; if(to!=pre) { dfs1(to,now,d+1); sz[now]+=sz[to]; if(sz[to]>sz[son[now]]) son[now]=to; } } } void dfs2(int now=1,int pre=1,int sp=1){ ltop[now]=sp;mp[now]=++cnt;remp[cnt]=now; if(son[now]) dfs2(son[now],now,sp); forn(i,now){ int to=e[i].to; if(to!=son[now]&&to!=pre) dfs2(to,now,to); } } void getchain(){dfs1();dfs2();} int lca(int x,int y){ for(;ltop[x]!=ltop[y];deep[ltop[x]]>deep[ltop[y]]?x=fa[ltop[x]]:y=fa[ltop[y]]); return deep[x]<deep[y]?x:y; }//@基础部分@ void update(int a,int b,int val){ while(ltop[a]!=ltop[b]){ if(deep[ltop[a]]<deep[ltop[b]])swap(a,b); tree.update(mp[ltop[a]],mp[a],val); a=fa[ltop[a]]; } if(deep[a]>deep[b])swap(a,b); tree.update(mp[a],mp[b],val); } int query(int a,int b,int k){ int sum=0; while(ltop[a]!=ltop[b]){ if(deep[ltop[a]]<deep[ltop[b]])swap(a,b); sum+=tree.query(mp[ltop[a]],mp[a],k); a=fa[ltop[a]]; } if(deep[a]>deep[b])swap(a,b); sum+=tree.query(mp[a],mp[b],k); return sum; } }g;
而且,分清楚算法的不同区域是很重要的,例如基础线段树的板子:
class segtree{public: #define nd node[now] #define ndl node[now<<1] #define ndr node[now<<1|1] struct segnode{ int l,r,val,tag; void update(int x){val+=x,tag+=x;} }node[maxn<<2|3]; int n; void pushup(int now){nd.val=min(ndl.val,ndr.val);} void pushdown(int now){ if(nd.tag){ ndl.update(nd.tag); ndr.update(nd.tag); nd.tag=0; } } void maketree(int s,int t,int now=1){ nd={s,t,a[s]}; if(s==t) return ; maketree(s,(s+t)>>1,now<<1); maketree(((s+t)>>1)+1,t,now<<1|1); pushup(now); } void update(int s,int t,int x,int now=1){ if(s<=nd.l&&t>=nd.r) {nd.update(x);return ;} pushdown(now); if(s<ndr.l) update(s,t,x,now<<1); if(t>ndl.r) update(s,t,x,now<<1|1); pushup(now); } int query(int s,int t,int now=1){ if(s<=nd.l&&t>=nd.r) return nd.val;/ pushdown(now); int ret=1e9; if(s<ndr.l) ret=query(s,t,now<<1); if(t>ndl.r) ret=min(query(s,t,now<<1|1),ret); return ret; } }tree;
对于这份模板,修改成区间+,区间覆盖,区间01翻转等等都非常直接:改2个push,和node内的信息即可,而且想开几棵树就开几棵树
还有一些其他的代码习惯,比如
我所有的树状数据结构,板子里的类名虽然都不一样,比如线段树segtree,树状数组bit等等,
但是我板子里的变量名都是tree,并且接口也都是maketree,update,query等等,然后其他算法,比如树链剖分,也是如此
第二步,就是比赛的实际应用了
很多时候,你都会随时修改你的代码仓库,但是你经常又有比赛,难道每次比赛都去强行弄一个word吗?
显然不需要,考虑你现在已经有一个目录,分门别类的存好了所有的代码,
那么,如果有一个程序,可以每次一键帮我把代码拿出来组合成一个可以直接打印的pdf该多好?
答案是有的,那就是$latex$
是用内嵌代码文件功能,并且用目录索引快速定位!
1.每次添加入新的模板时,只需要在$latex$源修改添加subsection即可
2.每次改旧的模板时,完全不需要改$latex$代码
只需要在需要用的时候,编译一遍$latex$源代码就行
我的基本设置是这样的:
减小行距,段距,减小了页边距,增加了stl关键字+下划线,并使用横版a4大小,三栏,显示行号,制表符2格大小,
代码内嵌中文使用了逃逸符号,用'@'将cpp文件内的汉字包起来即可
并且最终双面打印,这样展开看的更多,比赛的时候可以不用经常翻页
不会latex? 学就是啦, 比python写脚本简单多了!
一劳永逸,latex一时爽,一直latex一直爽!
参考:
论文写作的又一利器:VSCode + Latex Workshop + MikTex
配置默认设置的时候,按[ctrl+',']进入设置,
然后在整体设置下面修改json,在大括号里加入上面的latex配置就行,注意json的语法,别忘了逗号..
初步的效果如下:
latex源代码如下,我自己非常满意,想用可以直接套我的设置,
用miktex+vscode可以顺利编译:
1 \documentclass[UTF8]{ctexart} 2 \usepackage{ctex} 3 \usepackage{multicol} 4 \usepackage[landscape,margin=10mm,bottom=5mm,left=5mm,right=5mm,headsep=2mm]{geometry} 5 \usepackage[breaklinks,colorlinks,linkcolor=black,citecolor=black,urlcolor=black]{hyperref} 6 \usepackage{graphicx} 7 \usepackage[Glenn]{fncychap} 8 \usepackage{listings} 9 \usepackage{verbatim} 10 \usepackage{CJKutf8} 11 \usepackage{textcomp} 12 \usepackage{xcolor} 13 \usepackage{setspace} 14 \usepackage{fancyhdr} 15 \usepackage{titlesec} 16 \usepackage{multicol} 17 \usepackage{CJKutf8} 18 \geometry{footskip=5mm} 19 \title{ACM Standard Code Library} 20 \author{Nervendings NWU-ACMICPC QQ:419000977} 21 \definecolor{dkgreen}{rgb}{0,0.6,0} 22 \definecolor{gray}{rgb}{0.5,0.5,0.5} 23 \definecolor{mauve}{rgb}{0.58,0,0.82} 24 \definecolor{green2}{rgb}{0,0.6,0.4} 25 \definecolor{CPPLight} {HTML} {686868} 26 \definecolor{CPPSteel} {HTML} {888888} 27 \definecolor{CPPDark} {HTML} {262626} 28 \definecolor{CPPBlue} {HTML} {4172A3} 29 \definecolor{CPPGreen} {HTML} {487818} 30 \definecolor{CPPBrown} {HTML} {A07040} 31 \definecolor{CPPRed} {HTML} {AD4D3A} 32 \definecolor{CPPViolet} {HTML} {7040A0} 33 \definecolor{CPPGray} {HTML} {B8B8B8} 34 \pagestyle{fancy} 35 \lhead{CUMTB} 36 \lhead{\CJKfamily{hei} northwest university} 37 \chead{} 38 \rhead{Page \thepage} 39 \rhead{\CJKfamily{hei} Page \thepage} 40 \lfoot{} 41 \cfoot{} 42 \rfoot{} 43 \setlength{\columnsep}{10pt} 44 \renewcommand{\headrulewidth}{0.4pt} 45 \renewcommand{\footrulewidth}{0.4pt} 46 \setlength{\parskip}{0pt} 47 \setlength{\lineskip}{1pt} 48 \titlespacing{\section} {0pt}{0pt}{0pt} 49 \titlespacing{\subsection} {0pt}{0pt}{2pt} 50 \lstset{frame=tb, 51 language=c++, 52 aboveskip=0pt, 53 belowskip=0pt, 54 showstringspaces=false, 55 columns=flexible, 56 basicstyle={\ttfamily}, 57 numbers=left, 58 numberstyle=\small, 59 keywordstyle=\color[RGB]{40,40,255}\bfseries\underbar, 60 commentstyle=\it\color[RGB]{0,96,96}, 61 stringstyle=\rmfamily\slshape\color[RGB]{128,0,0}, 62 breaklines=true, 63 tabsize=2, 64 frame=single, 65 numberblanklines=false, 66 numbersep=5pt, 67 escapeinside=@@, 68 xleftmargin=5pt,xrightmargin=5pt, 69 framexleftmargin=1pt,framexrightmargin=0pt, 70 captionpos=t, 71 breaklines=true, 72 morekeywords={alignas,continute,friend,register,true,alignof,decltype,goto, 73 reinterpret_cast,try,asm,defult,if,return,typedef,auto,delete,inline,short, 74 typeid,bool,do,int,signed,typename,break,double,long,sizeof,union,case, 75 dynamic_cast,mutable,static,unsigned,catch,else,namespace,static_assert,using, 76 char,enum,new,static_cast,virtual,char16_t,char32_t,explict,noexcept,struct, 77 void,export,nullptr,switch,volatile,class,extern,operator,template,wchar_t, 78 const,false,private,this,while,constexpr,float,protected,thread_local, 79 const_cast,for,public,throw,std}, 80 emph={map,set,multimap,multiset,unordered_map,unordered_set, 81 unordered_multiset,unordered_multimap,vector,string,list,deque, 82 array,stack,forwared_list,iostream,memory,shared_ptr,unique_ptr, 83 random,bitset,ostream,istream,cout,cin,endl,move,default_random_engine, 84 uniform_int_distribution,iterator,algorithm,functional,bing,numeric,}, 85 emphstyle=\color{CPPViolet}\underbar, 86 } 87 \begin{document} 88 \begin{CJK}{UTF8}{<font>} 89 \begin{multicols}{3} 90 \begin{spacing}{0.80} 91 \begin{titlepage} 92 \maketitle 93 \thispagestyle{empty} 94 \pagebreak 95 \pagestyle{plain} 96 \tableofcontents 97 \end{titlepage} 98 \section{DataStruct} 99 \subsection{动态开点线段树} 100 \lstinputlisting{DataStruct/dynamic-segtree-1.cpp} 101 \subsection{区间第k大} 102 \lstinputlisting{DataStruct/kth.cpp} 103 \subsection{线段树优化建图} 104 \lstinputlisting{DataStruct/segtree-graph.cpp} 105 \subsection{线段树合并} 106 \lstinputlisting{DataStruct/segtree-merge.cpp} 107 \subsection{二维线段树} 108 \lstinputlisting{DataStruct/xysegtree.cpp} 109 \subsection{主席树} 110 \lstinputlisting{DataStruct/psegtree.cpp} 111 \subsection{Splay} 112 \lstinputlisting{DataStruct/SplayTree.cpp} 113 \subsection{Treap} 114 \lstinputlisting{DataStruct/treap.cpp} 115 \subsection{老司机树} 116 \lstinputlisting{DataStruct/ODT.cpp} 117 \subsection{可持久化01字典树} 118 \lstinputlisting{DataStruct/p-01trie.cpp} 119 \subsection{并查集非递归+按秩合并} 120 \lstinputlisting{DataStruct/ufs-2.cpp} 121 \subsection{并查集可删除版} 122 \lstinputlisting{DataStruct/ufs.cpp} 123 \subsection{ST表} 124 \lstinputlisting{DataStruct/s-table.cpp} 125 \subsection{扫描线算法} 126 \lstinputlisting{DataStruct/scanline-algorithm.cpp} 127 \subsection{树状数组} 128 \lstinputlisting{DataStruct/bit.cpp} 129 \section{Graph} 130 \subsection{最大流Dinic算法} 131 \lstinputlisting{Graph/dinic.cpp} 132 \subsection{费用流原始对偶算法} 133 \lstinputlisting{Graph/mncf-dij.cpp} 134 \subsection{费用流EK+spfa算法} 135 \lstinputlisting{Graph/mncf-spfa.cpp} 136 \subsection{Tarjan 求桥} 137 \lstinputlisting{Graph/tarjan-bridge.cpp} 138 \subsection{Tarjan 求割顶} 139 \lstinputlisting{Graph/tarjan-point.cpp} 140 \subsection{Dijkstra最短路} 141 \lstinputlisting{Graph/dij.cpp} 142 \subsection{重链剖分} 143 \lstinputlisting{Graph/Heavy-chain-subdivision-edge.cpp} 144 \subsection{欧拉路径} 145 \lstinputlisting{Graph/euler-path.cpp} 146 \subsection{三元环计数} 147 \lstinputlisting{Graph/3-circle.cpp} 148 \subsection{最小生成树Prim算法} 149 \lstinputlisting{Graph/prim.cpp} 150 \subsection{SW全局最小割算法} 151 \lstinputlisting{Graph/Stoer-Wagner.cpp} 152 \subsection{树上点分治} 153 \lstinputlisting{Graph/tree-v-div.cpp} 154 \section{Geometry} 155 \subsection{计算几何} 156 \lstinputlisting{Geometry/basic.cpp} 157 \section{Math} 158 \subsection{逆元,快速幂,快速乘} 159 \lstinputlisting{Math/math-bass.cpp} 160 \subsection{欧拉函数} 161 \lstinputlisting{Math/euler_phi.cpp} 162 \subsection{欧拉函数2} 163 \lstinputlisting{Math/euler.cpp} 164 \subsection{莫比乌斯函数} 165 \lstinputlisting{Math/mobius.cpp} 166 \subsection{米勒罗宾素数判定} 167 \lstinputlisting{Math/Miller-Rabin.cpp} 168 \subsection{大整数因子分解} 169 \lstinputlisting{Math/Pollard-Rho.cpp} 170 \subsection{拉格朗日插值} 171 \lstinputlisting{Math/polysum.cpp} 172 \subsection{异或线性基} 173 \lstinputlisting{Math/linear-bass.cpp} 174 \subsection{线段树维护区间线性基} 175 \lstinputlisting{Math/segtree-linearbass.cpp} 176 \section{String} 177 \subsection{KMP} 178 \lstinputlisting{String/kmp.cpp} 179 \section{Others} 180 \subsection{快速读写} 181 \lstinputlisting{Others/fastio.cpp} 182 \subsection{LIS} 183 \lstinputlisting{Others/lis.cpp} 184 \subsection{矩阵} 185 \lstinputlisting{Others/matrix_pow.cpp} 186 \subsection{头文件} 187 \lstinputlisting{Others/head.cpp} 188 \subsection{莫队算法} 189 \lstinputlisting{Others/mo-algorithm.cpp} 190 \end{spacing} 191 \end{multicols} 192 \end{CJK} 193 \end{document}
但是每次做模板都要很麻烦的复制粘贴,所以可以考虑更方便的做法,
那就是写一个脚本,直接抓取同文件夹下的源代码,组合成一个latex源代码文件
这里提供一个简单的参考实现
head为默认的环境配置,tail为默认的配置结束部分
config即为各个section的内容,每次generator在同目录下抓取config,head,tail,读取配置信息,然后生成一个tex源文件,
最后编译一下就可以了
generator源代码:
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) using namespace std; const int maxn=1000,maxm=100; const char *index_name = "/config"; char section[maxm][maxn],temp[maxn]; char section_name[maxn],file_name[maxn]; int name_len,fill_len,count_secn; FILE* tex_source = NULL; void get_section(const char *s) { name_len=fill_len=0; int pos=0; while(s[pos]&&s[pos]!='<') ++pos; if(s[pos]) ++pos; while(s[pos]&&s[pos]!='>') section_name[name_len++]=s[pos++]; while(s[pos]&&s[pos]!='<') ++pos; if(s[pos]) ++pos; while(s[pos]&&s[pos]!='>') file_name[fill_len++]=s[pos++]; section_name[name_len]=file_name[fill_len]=0; } void read_dir(){ FILE* dir=fopen(index_name+1,"r"); count_secn=0; while(fgets(section[count_secn++],maxn,dir)!=NULL); fclose(dir); while(section[count_secn-1][0]=='\0'&&count_secn>0) --count_secn; rep(i,0,count_secn-1) if(section[i][strlen(section[i])-1]!='\0') section[i][strlen(section[i])-1]='\0'; } void put_file(const char *filename){ FILE *fp=fopen(filename,"r"); while(fgets(temp,maxn,fp)!=NULL)fprintf(tex_source,temp); fclose(fp); } signed main() { auto start_time=chrono::high_resolution_clock::now(); tex_source=fopen("ICPC-StdLibrary.tex","w"); put_file("head.in");read_dir(); printf("section number:%d\n",count_secn); rep(i,0,count_secn-1){ fprintf(tex_source,"\\section{%s}\n",section[i]); memcpy(temp,section[i], sizeof temp); strcat(temp,index_name); printf("section-%d {%s}:\n",i+1,section[i]); FILE* fp=fopen(temp,"r"); int cnt=0; while(fgets(temp,maxn,fp)!=NULL){ if(temp[0]!='<')break; get_section(temp); printf("subsection-%d {%s}:",++cnt,file_name); fprintf(tex_source,"\\subsection{%s} ",section_name); memcpy(temp, section[i], sizeof temp); temp[strlen(temp)+1] = 0; temp[strlen(temp)] = '/'; strcat(temp,file_name); fprintf(tex_source,"\\lstinputlisting{%s}\n",temp); puts(" Done"); } printf("section-%d {%s}: Done\n",i+1,section[i]); fclose(fp); } put_file("tail.in");fclose(tex_source); puts("Well Done"); auto end_time=chrono::high_resolution_clock::now(); cout<<"\nrun time: "<<chrono::duration<double,milli>(end_time-start_time).count()<<" ms\n"; getchar(); }