Github建库流程



夕阳无限好

Eason


阅读本文前应具有对Linux系统一定程度的了解.

本文面向第一次使用Git与Github的用户编写.

本文阅读时间可能偏长.

本文不是面向Linux系统以外的环境的教程.

Git

无论是txt、word、excel还是你的代码以及程序,只要涉及到版本管理那么便会让人搔首踟蹰、力不从心.

不信可视下文,

譬如我某次写的立项书:

大学生寒假社会实践立项书.xsxl
大学生寒假社会实践立项书_电子版.xsxl
大学生寒假社会实践立项书_书面版.xsxl
大学生寒假社会实践立项书_电子版_1.xsxl
大学生寒假社会实践立项书_电子版_2.xsxl
大学生寒假社会实践立项书_电子版_3.xsxl
大学生寒假社会实践立项书_书面版_1.xsxl

不仅含有两个分支,而且每次修改都要在文件名后置一个下标来标注;现在还只是管理两个分支总共两个文件的小项目,等到了有管理几百、几千、几万个文件的项目的需求时,这种做法实在是令人焦头烂额。

在此我们引入版本管理工具 Git,通过使用Git便可以极为方便的对项目版本进行管理

//没错(( 你的学术垃圾也可以用Git管理

Git ( /ɡɪt/ )[8] 是一个分布式版本控制系统[9],用于跟踪文件的版本。它通常被程序员用来控制源代码,以便协作开发软件。

Git 的设计目标包括速度、数据完整性以及对分布式、非线性工作流程的支持——成千上万的并行分支在不同的计算机上运行。[10][11][12]

与大多数其他分布式版本控制系统一样,与大多数客户端-服务器系统不同,Git 保持整个代码库(即 repo)的本地副本,具有历史记录和版本跟踪功能,独立于网络访问或中央服务器。每台计算机上的 repo 存储在一个标准目录中,并包含额外的隐藏文件以提供版本控制功能。Git 提供了在共享历史的 repo 之间同步更改的功能;这些 repo 是相互复制(克隆)而来的。为了协作,Git 支持与远程机器上的 repo 进行同步。尽管所有具有相同历史的 repo 是对等的,但开发人员通常使用中央服务器来托管一个 repo,以保存集成副本。

Git 是一个在 GPL-2.0-only 许可证下共享的免费开源软件。

Git 最初是由 Linus Torvalds 为 Linux 内核的开发而创建的版本控制工具。[14] "Git"商标由软件自由保护协会注册,标志着其在开源社区的正式认可和持续发展。

今天,Git 是事实上的标准版本控制系统。它是最受欢迎的分布式版本控制系统,截至 2022 年,近 95% 的开发者报告称它是他们的主要版本控制系统。它是专业开发者中使用最广泛的源代码管理工具。提供 Git 仓库服务的有 GitHub、SourceForge、Bitbucket 和 GitLab。

— 摘自wikipedia

Git的安装与配置

若想使用Git,首先得安装Git

Linux系统可在终端键入以下命令一键安装:

$ sudo dnf install git-all

Debian的发行版如Ubuntu请使用apt

$ sudo apt install git-all

Windows系统请访问官网进行安装 Git 重申:本文不是面向Linux系统以外的环境的教程

安装完成后可以用以下命令检查是否安装成功

$ git -v

在正式使用之前,需要先使用git config配置邮箱与用户名

$ git config ---global user.name "username"
$ git config ---global user.email mail@mailaddress.domain

config 后的第一段 也就是–global的位置是标识配置的作用范围的标识符,其中

省略: 如果省略此项,则对本地仓库(当前仓库)生效

–global: 填入此项则为全局配置,对所有的仓库生效

–system: 填入此项则为系统配置,对所有的用户生效

第一行双引号内部的字符串常量username为你的用户名

第二行最后一块内容为你的邮箱,此两项自行修改即可

输入以下命令可以保存,避免每次使用都需要进行设置[存在安全隐患]

$ git config --global credential.help store

一切就绪后可以使用如下命令显示先前的设置确保无误

$ git config --global --list

开始建库

一切就绪以后我们就可以使用Git新建本地仓库了

Repositories意为版本库/仓库,我们取首四字符repo作简称,表示一个项目的仓库.

repo类似一个文件夹,但其内部所有的内容 全部都被Git所维护

每个文件的修改 删除 以及添加文件的操作 都可以被Git追踪到

这就可以让我们很方便的追踪项目历史 或是回溯先前的版本

创建一个repo非常简单 只需要一条7个字符的命令 然后就可以把一个文件夹变成git所管理的本地仓库

$git init

或者,你可以直接从远程服务器上clone一个以及存在的仓库

$ git clone /* url */

值得注意的是,不同版本的Git在init一个项目的时候 主要分支的名称可能不一样,可能为master或main,这两个名称是等价的,因此无需在意此方面的偏差.

你也可以在config里设置此项,来令初始的主分支自动命名为main

$ git config --global init.defaultBranch main

此时使用ls -a命令可以看到一个名为.get的隐藏文件夹 便说明本地仓库已经建立完成

\rm -rf .get删除 .git文件可以表明当前文件夹不再是一个repo

git init 后还可以填入指定的文件名称 如果填入了则会在当前文件夹下新建一个指定名称的文件夹 此文件夹内部包含一个.get文件夹

Git的工作区域与文件状态

Git的本地数据管理分为三个区域

Git
|---------工作区 Working Directory
|---------暂存区 Starting Area/Index
|---------本地仓库 Local Repositories

工作区 也叫工作目录或本地目录,资源管理器看到的repo所在的文件夹就是一个工作区

暂存区 是一种临时存储区域 用于保存即将提交到Git仓库的修改内容 类似C++ 输入输出流中的缓冲区 暂存区是在Git进行版本控制时非常重要的一个区域 .git/index

本地仓库 即为使用git init 建立的仓库 其包含了完整的项目历史和元数据 .git/objects

完成一次文件的编辑后 我们需要使用一次git add 把变更的信息添加到暂存区内

然后再使用git commit把信息从暂存区移至本地仓库

在这个过程中我们可以使用Git的查看、比较以及撤销操作来保证版本控制的准确性和完整性

于是相应的 Git管理的文件存在以下几种状态

1.未跟踪 Untrack
2.未修改 Unmodified
3.已修改 Modified
4.已暂存 Staged

未跟踪状态的文件是没有被Git所管理的文件

未修改状态的文件是已经被Git所管理但内容没有发生变化

已修改状态的文件是已经被Git所管理且内容已经发生变化但未放入暂存区的文件

已暂存状态的文件是修改后且已经放入暂存区的文件

添加和提交文件

在了解上述内容后,我们就可以尝试为repo添加和提交文件了

总共涉及三个基础命令

$ git status     查看仓库状态
$ git add         添加文件
$ git commit 提交文件

在一个刚刚init的空repo中 键入git status会是如下情景

On branch main
No commits yet
nothing to commit (create/copy files and use "git add" to track)

此时文件夹为空,添加任一文件后status中会出现一列Untrack files内容(自行尝试)其中的文件/文件夹会被标红

使用git add命令可以追踪一个Untrack的文件 将其添加到暂存区内

status中便会出现一行标绿的new file ,此时新文件就已经被置入了暂存区

放入暂存区后文件还需要提交 这时候就要使用git commit了 我们的文件只有提交到本地仓库之后才真正的被git所管理了起来

commit只能对暂存区的文件进行操作 同时每commit一次都需要提供一个信息 具体操作如下

$ git commit -m "message" filename

如果不提供-m选项则会进入一个交互式界面 默认会使用vim来编辑提交的message

git add 与git commit还可以接受通配符作为文件名的某一部分来进行操作

如过要查看commit历史 则可以键入如下命令

$ git log
$ git log --oneline 此参数可以将每次commit的信息放在一行内 

回退版本

仅仅是添加和提交还不能满足我们项目的需求

倘若某天我们提交了一个错误文件,此时我们又没有先前文件的备份

仅仅依靠脑力弥补是不能胜任的

我们于是又引入一个命令

$ git reset --soft
$ git reset --hard
$ git reset --mixed

reset 命令用于回退版本 其中

soft参数用于表示回退到某一个版本 并且保留工作区和暂存区的所有修改内容

hard参数用于表示回退到某一个版本 并且丢弃工作区和暂存区的所有修改内容

mixed参数用于表示回退到某一个版本 并且丢弃暂存区但保留工作区的所以修改内容,mixed参数同时也是reset的默认参数,也就是说不置选项时会默认为mixed模式

如果我们不小心–hard回退了一个版本 则可以通过reflog查看操作之前的版本号之后再用reset回退

$ git reflog

查看差异

在此引入一个命令 diff

$ git diff

diff不仅可以查看 工作区 暂存区 本地仓库之间的差异

还可以查看 不同版本、不同分支之间的差异

有很多GUI工具可以很方便的图形化查看它们之间的差异

BCSP-X大题题解

T2-1

#include <cstdio>//单调栈
using namespace std;
int n, ans, stk[8][105], p[8], i, j;
int main(){/
    scanf("%d", &n);
    while (n--) {
        scanf("%d%d", &i, &j);//给定第i个栈和插入栈的值
            while (stk[i][p[i]] > j) {//如果破坏了单调性就弹出元素至单调状态
                --p[i];
                ++ans;
            }
        if (stk[i][p[i]] != j) {//此时已满足单调性 插入元素移动下标
            stk[i][++p[i]] = j;
            ++ans;
        }
    }
    printf("%dn", ans);
    return 0;
}
(16)若将第002行的“using namespace std;”删去,程序仍可以正常执行,且对于相同的输入数据,输出结果不变(✔)

本段代码使用了C语言的标准 不需要使用命名空间stl

(17)若将第003行的“int”改为“long long”,程序仍可以被正常执行,且对于相同的输入数据,输出结果不变(✗)

int为32位有符号整型 long long为64位有符号整型 数据大于 $2^{32}$ 的时候int会发生溢出

(18)若将第 013 行的“++p[i]”改为“p[i]++”,程序仍可以被正常执行,且对于相同的输入数据,输出结果不变( ✗)

“++p[i]” 执行p[i]+=1 并返回p[i]本身这一左值

“p[i]++” 执行p[i]+=1 并返回执行前p[i]的拷贝这一右值

++p[i]由于参与表达式内部运算,改变语义会导致数据变化

(19)若将第 014 行的“++ans”改为“ans++”,程序仍可以被正常执行,且对于相同的输入数据,输出结果不变(✔)

同上,++ans本身不作为表达式内部变量参与运算,故无影响

(20)程序输出的 ans 值不可能为 0(✔)

若输入的n==0,则ans被值初始化为0,反之则不为0,输入规则内指出所有数据为正整数,故不为0

(21)该程序的算法时间复杂度为($O(n)$ )

可将stk[8][105]视作 8个元素的所含数据的集合 ,近似于

struct STKS{
    int num[105]
}stk[8];

内层while在常数级别的p[i]≈105的下标上运动,不受输入量n影响

外循环为执行n次,故复杂度为O(n)

(22)程序输出的 ans 值最大可能为(2n-1)

最大的简单情况为插入n-1个单调的元素后再插入一个小于第一个元素的元素

插入最后一个元素前此时由于先前未破坏单调性 ans == n-1

插入最后一个元素后内层while又执行了n-1次,此时ans==2n-2

if语句插入后,ans+=1,ans==2n-1.

具体原因为:每个元素至多进栈一次 ,出栈一次,最后一个元素入栈时已经完成了n次操作,故而不能出栈,理想情况下为2n-1次对ans的自增

又已知理想情况存在,故 $max(ans)=2n-1$

T2-2


#include<cstdio>
using namespace std;
bool valid(int k,int m){
    int now=0;
    for(int i=1;i<=k;i++){
        now = (now + m) % (2 * k - i + 1);//约瑟夫环?
            if(now==0)
                now=2*k-i+1//now仅可取值[1,2k-i+1]
            if(now<=k)
                return false;
            now--;
    }
    return true;
}

int main(){
    int k;
    cin>>k;
    int m=k + 1;
    while(!valid(k,m))
        m++;
    cout<<m;
    return 0;
}
(23)valid 函数的算法时间复杂度为 O(k)(此处的 k 特指程序输入的 k)(✔)
内层循环最多处理k次,正确
(24)若将第 005 行改为“for (int i = 0; i < k; i++) {”,程序仍可以被正常执行,且对于相同的输入数据,输出结果不变。(✗)
等价于i在循环内部表达式内减1,尚无此同余定理 ,错误
(25)若将第 018 行的“k + 1”改为“k”,程序执行或许会变慢,但仍可以被正常执行,且对于相同的输入数据,输出结果不变。(✗)

代入特殊值k=0得出1,修改后得出0

(26)程序输出的m值最小可能为(1)。

同上,m>=k+1,故最小为1

(27)若输入的k为4,程序将输出(32)

**作者未找到除代入以外的更优解!

整理valid可得合法情况为:

$$
forall J{i,m}=(J{i-1,m}+m-1)mod(2k-i+1) ~~ {J_{i,m}∈[k+1,2k-i+1]} ~~ i∈[1,k]
$$

可转化为

$$

\forall J_{i,m}=(J_{i-1,m}+m-1) \mod (2k-i) ~~ {J_{i,m} \in [k+1,2k]} ~~ i \in [0,k-1]

$$

不合法情况为:

$$
exist J{i,m}=(J{i-1,m}+m-1)mod(2k-i+1) ~~ {J_{i,m}∈[1,k]}~~i∈[1,k]
$$

(28)若输入的k为5,程序将输出( )

T2-3

#include<cstdio>
#include<cstring>
using namespace std;
int n,a[35],rt[35][35],val[35][35];

long long calc(int l,int r){
    if(l > r)
        return 1;
    if(val[l][r]!=-1)
        return val[l][r];
    if(l==r)
        val[l][r]=a[l];
        rt[l][r]=a[l];
    else{
        for(int i=l;i<=r;i++){
            if(calc(1,i-1)*calc(i+1,r)+a[i]>val[l][r]){
                val[l][r]=calc(l, i - 1) * calc(i + 1, r) + a[i];
                rt[l][r]=i;
            }
        }
    }
    return val[l][r];
}

void output(int l,int r){
    if (r<l)
        return;
    printf("%d",rt[l][r]);
    output(l,rt[l][r]-1);
    output(rt[l][r]+1,r);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    memset(val,-1,sizeof(val));
    printf("%lldn",calc(1,n));
    output(1,n);
    return 0;
}   
(29)该程序的算法的时间复杂度为$O(n^3)$(✔)

calc函数对每个子区间都进行了$O(n)$复杂度的选值,

总共有$n^2$个子区间,故总复杂度为$O(n^3)$

(30)该程序在执行过程中不会出现数据类型的溢出。( ✗)
calc函数返回long long类型,val采用int型存储,有溢出可能
(31)若输入数据能保证该程序在执行过程中不会出现数据类型的溢出,那么将第018行的“>”改为“>=”,对于相同的输入数据,程序输出的第一行结果不变。( ✔)
第一行为左右子区间乘积值+中间值的选择的最大值,>=不影响数据大小的动态规划,仅影响可替换的rt中的结构
(32)若n的输入为4,程序输出的第一行结果最大可能为( 1830 )

读入每个数都取最大值,最后求出来的乘积和才为最大值,

故输入数据为4/30 30 30 30 的情况下,顶层calc的四种情况为

a[1]为中间值 仅加上右子树的最大值30+30*30 最终为960

a[2]为中间值 $左子树右子树+a[2] = 30(30+30)+30 =1830$

a[3]为中间值 $左子树右子树+a[3]=30(30+30)+30=1830$

a[4]为中间值 仅加上左子树的最大值30+30*30 最终为960

(33)若输入数据为6 / 7 1 5 8 8 2(这里的斜线表示换行),程序输出的第二行结果为(A 2 1 4 3 5 6)

推理

排除C与D,因为二者实际为同一个划分策略

排除B,因为A优于B

比较 A 和 F 和 E A为407 F为368,E为128

(34)若将第29行代码与第31行代码互换位置,输入数据同上一题,程序输出的第二行结果为(6 5 3 4 1 2)。

前序表达式变成了倒序表达式 $右-左-根$

画出树状结构,即可得出表达式

或者直接倒置上题A选项,因为前序表达式与倒序表达式拼接恰好为回文字符串

T3-1

(1)生产任务

给定$n$种零部件,第$i$种零部件有$x_i$个。现在要用这些零部件生产机器。机器有两种。每生产一
台机器A需要$a_1$个零部件1,$a_2$个零部件2……$a_n$个零部件$n$。每生产一台机器B需要$b_1$个零部件1,
$b_2$个零部件2……$b_n$个零部件$n$。假设任意一个零部件都不可以被切割使用,请计算最多可以生产出多
少台机器。
输入数据保证$1 ≤ n ≤ 100000,1 ≤ x_i, a_i, b_i ≤ 109$。

#include<cstdio>
#include<algorithm>
using namespace std;

long long n,x[100005],a[100005],b[100005];

bool check(long long k){
    long long l=0,r=k;
    for (int i = 1; i <= n; i++) 
        if(___①___){ 
            if (___②___) 
                return false; 
        }
        else{
            long long p = x[i] - b[i] * k; 
            long long q = a[i] - b[i]; 
            if (a[i] > b[i]) 
            r = ___③___; 
            else 
            l = ___④___; 
            if (l > r) 
               return false; 
        }
    return true; 
}

int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&x[i]);    //每种零部件的数量
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);    //生产机器A对每种零部件的单位需求
    for(int i=1;i<=n;i++)
        scanf("%lld", &b[i]);  // 生产机器B对每种零部件的单位需求
    long long l = 0, r = 1e9; 
    while (l < r) {
        long long mid = (l + r + 1) / 2;
        ___⑤___ 
    }
    printf("%lldn", l);
    return 0; 
}

本题为基本二分算法

观察main函数中的l,r和mid声明,可以推测本题使用了二分

再次注意15行的$long~long~p~=~x[i]-b[i]*k$

可知$check(int~k)$通过代入B机器的生产数量计算A机器的生产数量

条件的两种状态影响下一次k的选择,$l==r$时候为全部机器的最大生产数量

(35)①处应填(E)

A.$a[i] < b[i]$
B.$a[i] <= b[i]$
C.$a[i] > b[i]$
D.$a[i] >= b[i]$
E.$a[i] == b[i]$
F.$a[i] != b[i]$

阅读程序可知未被覆盖的情况只有a[i]==b[i]

(36)②处应填(A)

A.$a[i] k > x[i]$
B.$a[i]
k < x[i]$
C.$a[i] x[i] > k$
D.$a[i]
x[i] < k$
E.$k x[i] > a[i]$
F.$k
x[i] < a[i]$

如果a[i]==b[i] 则造A,造B在i零件的情况下代价一样,仅拿一个做判断即可

答案在A、B之间,但由于尚不清楚返回后的操作,故先跳过

(37)③处应填(B)

A.$max(r, p / q)$
B.$min(r, p / q)$
C.$max(r, p / q + (int)(p~mod~q != 0))$
D.$min(r, p / q + (int)(p~mod~q != 0))$
E.$max(r, p / q + (int)(p ~mod~ q == 0))$
F.$min(r, p / q + (int)(p ~mod~ q == 0))$

首先,p为造了k台B机器后,剩下的零件量,若p为负则为需要的零件量

q为A,B机器所需零件的差值,

$$
frac{x[i]-k*b[i]}{a[i]-b[i]}=t
$$

$$
x[i]-ta[i]-(k-t)b[i]=left
$$

且有

$$
p~mod~q ~=~left
$$

故而

$$
x[i]-ta[i]-(k-t)b[i]=p~mod~q
$$

此时$p~mod~q$若等于0则说明x[i]已全部用光

若不为0,a[i]>b[i]时,a[i]-b[i]不能补充需要的零件量,仅能消耗剩下的零件量,故不操作,仅为p/q

r用于记录t能取的最大值,r初始值为k,若要更新仅能取小,故选择B

(38)④处应填(C)

A.max(l, p / q)
B.min(l, p / q)
C.max(l, p / q + (int)(p % q != 0))
D.min(l, p / q + (int)(p % q != 0))
E.max(l, p / q + (int)(p % q == 0))
F.min(l, p / q + (int)(p % q == 0))

分析同上,注意到此时a[i]<b[i],此时$p~mod~q$若不为0(小于或大于),,则剩下的零件可以多制造一台机器A,

l用于记录t能取的最小值,故选择C

(39)⑤处应填(C)

A.if (check(mid)) l = mid; else r = mid;
B.if (check(mid)) l = mid; else r = mid + 1;
C.if (check(mid)) l = mid; else r = mid – 1;
D.if (check(mid)) r = mid; else l = mid;
E.if (check(mid)) r = mid; else l = mid + 1;
F.if (check(mid)) r = mid; else l = mid – 1;

check在最大值小于最小值时返回false,说明k值太大应当减小,则36题为A

二分的目的是为了让数据区间减少一半,故而本题选择C而非A,B

T3-2

给定n个结点的无向图,结点编号1至n。图中共有n条边。每条边恰好连接着两个不同的结点。

每个结点恰好连接着两条无向边。现在,我们要对每个结点染色,但任意相邻的两个结点不可以被染
成相同的颜色。假设可选的颜色共有k种,请计算共有多少种不同的合法染色方案(对于两种方案,
假设存在某一编号的结点在两方案中被染了不同颜色,则认为这两种方案是不同的)。
输入数据保证1 ≤ n ≤ 100000,1 ≤ k ≤ 109。

#include<cstdio>
#include<cstring>
#include<algotithm>
using namespace std;

const int MOD =1e9+7;
int n,k,adj[100005][2],cnt[100005];
long long f[100005];
bool vis[100005];

long long fpow(int x,int k){//快速幂
    long long ans=1;
    for(;k;k>>=1,x=x*x%MOD)
        if(___①___)
            ans*=x;
    return ans;
}

int main(){
    scanf("%d%d",&n,&k);
    for(int i=1,a,b;i<=n;i++){
        scanf(%d%d",&a,&b);  // 每条无向边连接的两端结点
         if (adj[a][0]) adj[a][1] = b; 
            else adj[a][0] = b;
        if (adj[b][0]) adj[b][1] = a;
            else adj[b][0] = a;
    }
     for (int i = 1; i <= n; i++) {
        if (vis[i]) continue; 
        int len = 1, x = i; 
        vis[x] = true; 
        while (1) {
            if (adj[x][0] && !vis[adj[x][0]]) { 
                 x = adj[x][0];
                 vis[x] = true;
                 len++; 
            } 
            else if (adj[x][1] && !vis[adj[x][1]]) {
                x = adj[x][1]; 
                vis[x] = true;
                len++;
            }
            else break; 
        } 
        cnt[len]++; 
    } 
    f[1] = ___②___;
    f[2] = 1LL * k * (k - 1) % MOD;
    f[3] = ___③___; 
    for (int i = 4; i <= n; i++) 
         f[i] = ___④___; 
    long long ans = 1; 
    for (int i = 1; i <= n; i++) 
        if (cnt[i]) 
            ans = ___⑤___; 
    printf("%lldn", ans);
    return 0; 
}

离散数学中经典的染色问题
因为每个点都与两个不同的点连接,且有n个点和n条边,根据图论的知识可得知这是一张首尾相衔的简单环
输入数据中通过读入每条无向边连接的两端节点来构建此环
本题的预处理阶段具有极大的迷惑性,仿佛此图为一个森林,实则不然,因为n个点n条边的无向简单图只有一种可能

(40) ①处应填(D)

A. k / 2 == 0
B. k / 2 != 0
C. k % 2 == 0
D. k % 2 != 0
E. k & 2 == 0
F. k & 2 != 0

fpow函数是显而易见的快速幂
k%2!=0暨是k二进制的此刻最低位为1
此时可以用ans*=x,x取$1,2,4,8,16….2^n$

(41)②处应填(B)

A.1
B.k
C.k-1
D.k+1
E.k-2
F.k+2
f[1]对应了第一个点可染的颜色数 故选B

(42)③处应填(F)

A. 1LL k (k + 1) % MOD
B. 1LL (k + 1) (k + 2) % MOD
C. 1LL k k % MOD (k + 1) % MOD
D. 1LL
k k % MOD (k – 1) % MOD
E. 1LL k (k + 1) % MOD (k + 2) % MOD
F. 1LL
k (k – 1) % MOD (k – 2) % MOD

f[2]为长度为2的链可染的颜色数,直接为k*(k-1)

f[3]为长度为3的链可染的颜色数,易得出$k(k-1)(k-1)$ 并无此答案,考虑实际为长度为3的头部固定的环的染色数,

故得出$f[3]=k(k-1)(k-2)$

(43)④处应填(F)

A. (f[i – 1] (k – 1) + f[i – 2] (k + 2)) % MOD
B. (f[i – 1] (k – 2) + f[i – 2] (k + 1)) % MOD
C. (f[i – 1] (k + 1) + f[i – 2] (k – 2)) % MOD
D. (f[i – 1] (k + 2) + f[i – 2] (k – 1)) % MOD
E. (f[i – 1] (k – 1) + f[i – 2] (k – 2)) % MOD
F. (f[i – 1] (k – 2) + f[i – 2] (k – 1)) % MOD

见递推式,考虑当前问题与子问题的关系

结构上来说,长度为i的环仅仅比i-1的环多增加了一个点,但位置不同会导致染色数的不同

如果在$[a,a+1]~~~a∈[1,i-2]$之间插入,$f[i]~=f[i-2]*(k-1)$

如果在$[i-1,1]$之间插入,$f[i]~=f[i-1]*(k-2)$

加法原理可得$f[i]=f[i-1](k-2)+f[i-2](k-1)$

(44)⑤处应填(A)

A. (ans fpow(f[i], cnt[i])) % MOD
B. (ans + fpow(f[i], cnt[i])) % MOD
C. (ans
fpow(f[i], cnt[i] – 1)) % MOD
D. (ans + fpow(f[i], cnt[i] – 1)) % MOD
E. (ans * fpow(f[i], cnt[i] + 1)) % MOD
F. (ans + fpow(f[i], cnt[i] + 1)) % MOD

此时根据题意可知ans并不是一个头部固定的环的染色数,故需要对其排列,

本题具有迷惑性,实际上只需要对f[maxlen]的情况进行计算

现在只需要考虑每个点都为开头的情况,乘法原理可知只需要连续相乘即可计算

故$ans=f[maxlen]^{maxlen}$,正好是A

Welcome Amigo!

  • 本网站为个人网站,不接收用户注册与评论,在插件修改之前会自动删除用户和评论

远道而来的旅人 欢迎至此,

PsyChen 会不定期在此发布文章与代码 也许您就是为此而来.

社团的同学们可以使用公用账号上传/下载库中代码

希望大家可以玩的开心


AlgoLogy Algorithm Git Github HomePage HTML ICP PsyChen SiXuan-VisionGroup Student-Union UEM

ICP备案号:冀ICP备2024096922号