zoukankan      html  css  js  c++  java
  • 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一直爽!

    参考:

    LaTeX快速入门指南

    论文写作的又一利器:VSCode + Latex Workshop + MikTex

    论文杀器LaTeX用法汇总(3)——实战·ACM代码模板

    如何安装MiKTeX

    配置VSCode 作为LaTex 编辑器

    Latex中文utf-8编码的三种方式

    Latex插入代码并高亮显示

    配置默认设置的时候,按[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 	itle{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 
    head{Page 	hepage}
     39 
    head{CJKfamily{hei} Page 	hepage}
     40 lfoot{}
     41 cfoot{}
     42 
    foot{}
     43 setlength{columnsep}{10pt}
     44 
    enewcommand{headrulewidth}{0.4pt}
     45 
    enewcommand{footrulewidth}{0.4pt}
     46 setlength{parskip}{0pt}
     47 setlength{lineskip}{1pt}
     48 	itlespacing{section} {0pt}{0pt}{0pt}
     49 	itlespacing{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={	tfamily},
     57   numbers=left,
     58   numberstyle=small,
     59   keywordstyle=color[RGB]{40,40,255}fseriesunderbar,
     60   commentstyle=itcolor[RGB]{0,96,96},
     61   stringstyle=
    mfamilyslshapecolor[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 egin{document}
     88 egin{CJK}{UTF8}{<font>}
     89 egin{multicols}{3}
     90 egin{spacing}{0.80}
     91 egin{titlepage}
     92 maketitle
     93 	hispagestyle{empty}
     94 pagebreak
     95 pagestyle{plain}
     96 	ableofcontents
     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}
    View Code

    但是每次做模板都要很麻烦的复制粘贴,所以可以考虑更方便的做法,

    那就是写一个脚本,直接抓取同文件夹下的源代码,组合成一个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]==''&&count_secn>0) --count_secn;
      rep(i,0,count_secn-1) if(section[i][strlen(section[i])-1]!='')
        section[i][strlen(section[i])-1]='';
    }
    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
    ",count_secn);
      rep(i,0,count_secn-1){
        fprintf(tex_source,"\section{%s}
    ",section[i]);
        memcpy(temp,section[i], sizeof temp);
        strcat(temp,index_name);
        printf("section-%d {%s}:
    ",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}
    ",temp);
          puts(" Done");
        }
        printf("section-%d {%s}: Done
    ",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<<"
    run time: "<<chrono::duration<double,milli>(end_time-start_time).count()<<" ms
    ";
      getchar();
    }
    
  • 相关阅读:
    【docker】更换挂载目录
    【设计】交互走查表
    MySQL常用字符串函数
    VIM_manual
    MySQL操作符
    基础SELECT实例
    MySQL字符集及校对规则的理解
    Linux命令之tar-rsync
    Linux-PATH_环境变量
    MySQL常用数据类型
  • 原文地址:https://www.cnblogs.com/nervendnig/p/10915584.html
Copyright © 2011-2022 走看看