Giter VIP home page Giter VIP logo

tedukuri's Introduction

tedukuri

tedukuri 是「手作り」的意思,读作「てづくり」(近似英文 tezukuli),指的是由《算法竞赛进阶指南》的作者、读者一起,用自己的双手共同维护的资源社区。为此,我们选择了 GitHub 这个世界上最大的 Programmer Community 作为支持平台。

购书链接 Buy the Book

全新排版修订的《算法竞赛进阶指南》已经上线啦,请大家认准大象出版社和新的封面,出版社的官方网店是:https://item.jd.com/10067239761146.html

视频课 Videos

本书官方的视频教材正在陆续上线,大家可以前往 AcWing 学习。

Contents

目前,这个 repo 里包含以下内容:

  • 第一版(2018 年 1 月第一次印刷)最新勘误(更新于 2018 年 6 月 5 日)
  • 第二次修订(2018 年 6 月印刷)最新勘误(更新于 2018 年 11 月 26 日)
  • 第三次修订(2018 年 11 月印刷)最新勘误(更新于 2018 年 11 月 26 日)
  • 第四次修订(2019 年 4 月印刷)未提供勘误
  • 第五次修订(2019 年 9 月印刷,印数 13001-17000)未提供勘误

========== 本书前五次修订版已停止 QQ 答疑等技术支持,视频课基于第六版及以上,建议更换新版 ==========

  • 第六次修订(2020 年 8 月印刷,印数 17001-22000)最新勘误(更新于 2021 年 3 月 27 日)
  • 第七次修订(2021 年 8 月印刷,印数 22001-27000)暂无勘误
  • 本书每次印刷都有修订和更新(其中第二、三次修订变化较大),只是为了避免重新申请 ISBN 码,所以第 X 版在版权页标注的都是第一版第 X 次印刷
  • 包含本书专用题库、最新题目提交地址和标程、数据配备情况的附录表格
  • 最新配套内容(随时更新习题翻译、标程、测试数据、习题题解等)

相比原书第一版配套光盘,这个 repo 已经先后多次增加了数十道 POJ 题目和 BZOJ 题目的测试数据,以及 OJ 未收录题目的测试数据,请参照提交历史(commits)或者最新的附录 PDF。 相比第四次修订版配套光盘,这个 repo 于 2019 年 8 月 17 日补齐了全书 99% 题目的测试数据。

Instructions

如何下载 GitHub 上的资源?

  1. 可以直接点击页面上的绿色按钮"Clone or download",下载整个 zip 压缩包(由于你懂的原因,速度可能会比较慢)。
  2. 也可以在本地安装 Git 命令行或图形界面客户端,git clone(克隆)项目仓库,以后每次执行 git pull 都可以快速地增量更新。
  3. 还可以找到想要的文件,直接右键另存为(只能保存单个文件,不能保存文件夹,数据文件较多时会比较麻烦)。

如何做书中“OJ 未收录”或“来源于非主流 OJ”的题目?

可以访问 Contest Hunter 上的本书专用题库,这里对书中大部分例题、习题均有收录,并公开测试数据和所有用户提交的代码。另外 AcWing 也翻译并收录了本书全部题目。

如何做 BZOJ 隐藏题目?

不熟悉的读者可能觉得书上的部分题目在 BZOJ 上找不到,这是因为 BZOJ 隐藏了部分题目(又称为权限题),只对收费的 VIP 用户开放。

  1. 可以联系 BZOJ 管理员,购买 2 年 VIP 服务,价格在几百元左右,到了 NOI 阶段 BZOJ 上的题目有很高的价值。
  2. 你也可以访问 CH 上的本书专用题库,直接免费做到这些题目中的绝大部分(原本是国内外公开比赛的题目)。

BZOJ 现已停止运营,请自己寻找解决方案。

POJ 突然挂了怎么办?

可以去 OpenJudge 上的百练题库搜索一下题目标题,OpenJudge 也是北大运营的,题目与 POJ 有很大重合。 另外,本书专用题库已经实现了对 OpenJudge 的远端评测 (Virtual Judge),也可以直接提交。

为什么选用了很多老牌 OJ 的英文题目,英文捉鸡怎么办呢?

  1. 即使是国内正规的比赛、最近新兴的 OJ 上(Luogu, LibreOJ 等)的题目,很多也是翻译、借鉴自国外的 idea。对于同种 idea 的题目,出于尊重原创者的考虑,我们一般会以最早的来源为标准。
  2. 提高英文水平(至少到能看懂题面的程度)对于日后阅读科学文献和 PKU/THU 的选拔都很有帮助,很快你就会习惯了,不会耽误你的时间。
  3. 实在是觉得费劲的话,可以 Google/Bing 一下"POJXXXX"或题目标题等关键字,搜索引擎可能会给出别人总结的题意,甚至是在其他中文 OJ 上的翻译版本噢!不过缺点是你可能会不小心搜到题目的解法。

Contributions

除作者不断收集外,当读者获得数据或自己生成了数据,或者认为自己的解法独具一格、代码很具有参考价值时,非常欢迎为这个 repo 作出贡献。您的用户名将会永远地记录在提交历史 (commits) 中,并展示在 contributors 页面。您将对自己的改动负责,因此请确保其没有版权问题。

作为一名程序员,学习使用 Git 是一项基本生存技能。下面的步骤是给不熟悉 Git 相关流程的读者阅读的:

  • 注册 GitHub 账户,并 fork 这个 repo 的最新副本到自己的账户下。
  • clone 该副本到本地计算机,并创建一个新的分支 (branch)。
  • 作出改动,在自己的分支上提交 (commit),并写上一句简短、恰当的改动说明。
  • 打开一个 Pull Request,请求合并改动到原 repo,等待审核通过或得到修改意见。

更详细的指南请参考 Git 手册。您可能需要安装软件来获得 Git 命令行或图形化界面工具。

tedukuri's People

Contributors

alpha1022 avatar bubbleioa avatar chungzh avatar huyiyun2020 avatar junity233 avatar lydrainbowcat avatar planet6174 avatar ree-chee avatar reiaccept avatar wegfan avatar wfzwf avatar xeonacid avatar yunanzhu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tedukuri's Issues

发现两个小错误

P119右下角图
插入:1 2
应为
插入:12
P437图一
图中D->B边容量应为6

0x12 队列

0x12 队列里,说 C++中的queue就是一个循环队列。这句话应该是有问题的,Ring buffer 还没有进入STL

A minor bug in the definition of BST.

On page 233,

  1. 该节点的关键码不小于它的右子树中任意节点的关键码
  2. 该节点的关键码不大于他的右子树中任意节点的关键码

But in the following:

显然,二叉查找树的中序遍历时一个关键码单调递增的节点序列。

To avoid ambiguity, and meet the above definition, we should use 不严格单调递增 instead of 单调递增, as 单调递增 means \forall x, y, f(x) < f(y).

第四版错误

第四版第17页,他的数据范围写的是 1 <= a,b <= 5 * 10 ^ 7
而题目应该是0 <= a,b <= 5 * 10 ^ 7

对于书中描述的Astar算法的疑问

设每个节点x到最终节点的实际最短代价为g(x)
设有估价函数f(x), 其对于节点1~n的估价 f(1)⋯f(n) 有:
f(x)≤g(x)
在此前提下,是否必须满足以下条件:

若g(a)≤g(b), 则必有f(a)≤f(b)
才能保证最终节点第一次被从堆中取出时得到的一定是最优解?

关于书上dinic剪枝优化的问题

题目:P1231 教辅的组成

使用的是书上的dinic模板,改成了用vector实现,总体代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF = 1 << 30, N = 40010;
int n1, n2, n3, m1, m2, s, t;
struct Edge {
    int from, to, flow;
    Edge(int u, int v, int f) : from(u), to(v), flow(f) {}
};
vector<Edge> edges;
vector<int> G[N];
void addEdge(int from, int to, int flow) {
    edges.push_back((Edge){from, to, flow});
    edges.push_back((Edge){to, from, 0});
    G[from].push_back(edges.size() - 2);
    G[ to ].push_back(edges.size() - 1);
}

int d[N];
queue<int>q;
bool bfs() {
    memset(d, 0, sizeof(d));
    while (!q.empty()) q.pop();
    q.push(s), d[s] = 1;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        if (x == t) return true;
        for (int i = 0; i < G[x].size(); ++i) {
            Edge &e = edges[G[x][i]];
            int to = e.to;
            if (!d[to] && e.flow)
                q.push(to), d[to] = d[x] + 1;
        }
    }
    return false;
}
int Dinic (int x, int flow) {
    if (x == t) return flow;
    int rest = flow;
    for (int i = 0; i < G[x].size(); ++i) {
        Edge &e = edges[G[x][i]], &fe = edges[G[x][i]^1];
        int to = e.to;
        if (e.flow && d[to] == d[x] + 1) {
            int k = Dinic(to, min(rest, e.flow));
            //if (!k) d[to] = 0;
            e.flow -= k, fe.flow += k;
            rest -= k;
        }
    }
    //if (rest == flow) d[x] = 0;
    return flow - rest;
}
int main()
{
    scanf("%d%d%d", &n1, &n2, &n3);
    scanf("%d", &m1);
    for (int i = 1; i <= n1; ++i)
        addEdge(n2 + i, n1 + n2 + i, 1);
    for (int i = 1; i <= m1; ++i) {
        int u, v;
        scanf("%d%d",&u, &v);
        addEdge(v, n2 + u, 1);
    }
    scanf("%d", &m2);
    for (int i = 1; i <= m2; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        addEdge(n1 + n2 + u, 2 * n1 + n2 + v, 1);
    }
    s = 0, t = 2 * n1 + n2 + n3 + 1;

    for (int i = 1; i <= n2; ++i)
        addEdge(s, i, 1);
    for (int i = 1; i <= n3; ++i)
        addEdge(2 * n1 + n2 + i, t, 1);

    //solve
    int maxflow = 0;
    int flow;
    while (bfs())
        while (flow = Dinic(s, INF)) maxflow += flow;

    //output
	printf("%d\n", maxflow);
	return 0;
}

其中 Dinic有三种写法:

第一种,无优化,AC

int Dinic (int x, int flow) {
    if (x == t) return flow;
    int rest = flow;
    for (int i = 0; i < G[x].size(); ++i) {
        Edge &e = edges[G[x][i]], &fe = edges[G[x][i]^1];
        int to = e.to;
        if (e.flow && d[to] == d[x] + 1) {
            int k = Dinic(to, min(rest, e.flow));
            //if (!k) d[to] = 0;
            e.flow -= k, fe.flow += k;
            rest -= k;
            if (rest == 0) break;
        }
    }
    //if (rest == flow) d[x] = 0;
    return flow - rest;
}

第二种,加上第一类剪枝,T飞了

int Dinic (int x, int flow) {
    if (x == t) return flow;
    int rest = flow;
    for (int i = 0; i < G[x].size(); ++i) {
        Edge &e = edges[G[x][i]], &fe = edges[G[x][i]^1];
        int to = e.to;
        if (e.flow && d[to] == d[x] + 1) {
            int k = Dinic(to, min(rest, e.flow));
            if (!k) d[to] = 0;
            e.flow -= k, fe.flow += k;
            rest -= k;
            if (rest == 0) break;
        }
    }
    //if (rest == flow) d[x] = 0;
    return flow - rest;
}

第三种,加上第二类剪枝(其实我觉得就是把第一类剪枝挪了个地方,本质上一样),AC了

int Dinic (int x, int flow) {
    if (x == t) return flow;
    int rest = flow;
    for (int i = 0; i < G[x].size(); ++i) {
        Edge &e = edges[G[x][i]], &fe = edges[G[x][i]^1];
        int to = e.to;
        if (e.flow && d[to] == d[x] + 1) {
            int k = Dinic(to, min(rest, e.flow));
            //if (!k) d[to] = 0;
            e.flow -= k, fe.flow += k;
            rest -= k;
            if (rest == 0) break;
        }
    }
    if (rest == flow) d[x] = 0;
    return flow - rest;
}

请问是否可以解答一下

1.为啥加上优化之后,似乎跑的反而没有朴素方法快啊(在别的题目上面,优化还是很明显的)

2.第一第二类剪枝,看起来似乎一模一样,就是把一个语句换了个地方,执行顺序几乎也是一致的,为啥运行效率却有天壤之别呢?

0x20 搜索 例题 八数码的疑问

仓库代码中使用cantor展开的代码,其中方向选择使用的是上左下右(uldr)
int dx[4] = {-1,0,1,0};
int dy[4] = {0,-1,0,1};
经在acwing上测试发现在仅改变方向选择顺序后,比如选择上下左右或上右下左(我并没有测试所有的顺序),会得到不同(错误)的结果,虽然都能搜索得到最终序列,但搜索长度会变长,debug了很久,发现只跟搜索顺序有关,附上用例和改变的代码
4 8 x 3 7 5 6 2 1

//Author:XuHt
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 362886;
int fa[N], f[N];
bool v[N];
// int dx[4] = {-1,0,1,0};
// int dy[4] = {0,-1,0,1};
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int jc[10] = {1,1,2,6,24,120,720,5040,40320,362880};
struct P {
	int i, x, y;
	string s;
	bool operator < (const P a) const {
		return x + y > a.x + a.y;
	}
};
priority_queue<P> q;

int cantor(string st) {
	int len = st.size();
	for (int i = 0; i < len; i++)
		if (st[i] == 'x') {
			st[i] = '0';
			break;
		}
	int ans = 1;
	for (int i = 0; i < len; i++) {
		int num = 0;
		for (int j = 0; j < i; j++) if (st[j] < st[i]) ++num;
		ans += (st[i] - '0' - num) * jc[len-i-1];
	}
	return ans;
}

int S(string s) {
	int ans = 0;
	for (unsigned int i = 0; i < s.size(); i++) {
		int r = i / 3, c = i % 3;
		if (s[i] == 'x') ans += abs(r - 2) + abs(c - 2);
		else {
			int k = s[i] - '1';
			ans += abs(r - k / 3) + abs(c - k % 3);
		}
	}
	return ans;
}

bool bfs(string st) {
	string ed = "12345678x";
	memset(v, 0, sizeof(v));
	memset(fa, -1, sizeof(fa));
	memset(f, -1, sizeof(f));
	int k;
	for (int i = 0; i < 10; i++)
		if (st[i] == 'x') {
			k = i;
			break;
		}
	P p;
	p.i = k;
	p.x = 1;
	p.y = S(st);
	p.s = st;
	q.push(p);
	v[cantor(st)] = 1;
	while (q.size()) {
		p = q.top();
		if (p.s == ed) return 1;
		q.pop();
		int r = p.i / 3, c = p.i % 3;
		for (int i = 0; i < 4; i++) {
			int nx = r + dx[i], ny = c + dy[i];
			if (nx < 0 || nx > 2 || ny < 0 || ny > 2) continue;
			string s = p.s;
			swap(s[p.i], s[nx*3+ny]);
			if (v[cantor(s)]) continue;
			v[cantor(s)] = 1;
			fa[cantor(s)] = cantor(p.s);
			f[cantor(s)] = i;
			P np;
			np.i = nx * 3 + ny;
			np.x = p.x + 1;
			np.y = S(s);
			np.s = s;
			q.push(np);
		}
	}
	return 0;
}

int main() {
	string st = "";
	for (int i = 1; i < 10; i++) {
		char s[2];
		cin >> s;
		st += s[0];
	}
	int cnt = 0;
	for (unsigned i = 0; i < st.size(); i++)
		if (st[i] != 'x')
			for (unsigned int j = 0; j < i; j++)
				if (st[j] != 'x' && st[j] > st[i]) ++cnt;
	if (cnt & 1) {
		cout << "unsolvable" << endl;
		return 0;
	}
	vector<int> ans;
	if (bfs(st)) {
		int k = cantor("12345678x");
		while (k != -1) {
			ans.push_back(f[k]);
			k = fa[k];
		}
		for (unsigned int i = ans.size() - 1; i < ans.size(); i--)
// 			if (ans[i] == 0) putchar('u');
// 			else if (ans[i] == 1) putchar('l');
// 			else if (ans[i] == 2) putchar('d');
// 			else if (ans[i] == 3) putchar('r');
			if (ans[i] == 0) putchar('u');
			else if (ans[i] == 1) putchar('r');
			else if (ans[i] == 2) putchar('d');
			else if (ans[i] == 3) putchar('l');
	} else puts("unsolvable");
	return 0;
}

Count The Repetition 中 st 的枚举是否必要

动态规划 倍增 CH5702 Count The Repetition 中,处理答案时枚举 st 似乎是不必要的,似乎 st = 0 的时候一定能够保证不比以后更劣

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cmath>
#include<string>
using namespace std;
string s1, s2;
int n1, n2;
long long f[105][32];

void solve() {
	memset(f, 0, sizeof(f));
	for (int i = 0; i < s1.size(); i++) {
		int pos = i;
		f[i][0] = 0;
		for (int j = 0; j < s2.size(); j++) {
			int cnt = 0;
			while (s1[pos] != s2[j]) {
				pos = (pos + 1) % s1.size();
				if (++cnt >= s1.size()) { 
					cout << 0 << endl; 
					return;
				}
			}
			pos = (pos + 1) % s1.size();
			f[i][0] += cnt + 1;
		}
	}
	for (int j = 1; j <= 30; j++)
		for (int i = 0; i < s1.size(); i++)
			f[i][j] = f[i][j - 1] + f[(i + f[i][j - 1]) % s1.size()][j - 1];
	long long m = 0;
	for (int st = 0; st < s1.size(); st++) { // HERE
		long long x = st, ans = 0;
		for (int k = 30; k >= 0; k--) {
			if (x + f[x%s1.size()][k] <= s1.size()*n1) {
				x += f[x%s1.size()][k];
				ans += 1 << k;
			}
		}
		m = max(m, ans);
	}
	cout << m / n2 << endl;
}

int main()  {
	while(cin >> s2 >> n2 >> s1 >> n1) solve();
}

Mondriaan's Dream 比书中代码更快的做法

#include<bits/stdc++.h>
using namespace std;
const int maxn = 13;

int n,m;
long long f[maxn][1<<maxn];

int a[1<<maxn], ct;

void solve()
{
    
    ct = 0;
    for(int i=0; i<(1<<m); ++i)
    {
        int cnt=0, is_odd=0;
        for(int j=0;j<m;++j)
          if((i>>j)&1) cnt^=1;
          else is_odd|=cnt, cnt=0;
        is_odd|=cnt;
        if(is_odd==0) a[++ct]=i;
    }
    
    memset(f, 0, sizeof f);
    for(int i=0; i<(1<<m); ++i)
    {
        int cnt=0, is_odd=0;
        for(int j=0;j<m;++j)
          if((i>>j)&1) is_odd|=cnt, cnt=0;
          else cnt^=1;
        is_odd|=cnt;
        if(is_odd==0) f[1][i]=1;
    }
    for(int i=2; i<=n; ++i)
        for(int j=0; j<(1<<m); ++j)
            for(int k=1; k<=ct; ++k) {
                if(j & a[k]) continue;
                int nowtmp = j | a[k];
                nowtmp = nowtmp ^ ((1<<m)-1);
                f[i][nowtmp] += f[i-1][j];
            }
    cout << f[n][0] << '\n';
}

int main()
{
    
    while(scanf("%d%d", &n, &m) != EOF && n) solve();
    return 0;
    
}

数据结构进阶 可持久化 Trie 例题 最大异或和代码的疑问

该题为了要满足 l - 1 <= p,这个条件,使用了latest数组记录最新最大值,但是因为是持久化Trie,每次都会新建一个节点[q][c],++tot,tot是递增的,那么latest[q]最终一定等于i (这是我的理解),那么那个max的意义在哪里,什么情况下max后的结果latest[q] != i?这题改了也能AC,是因为数据太弱的原因吗?

void insert(int i, int k, int p, int q) {
	if (k < 0) {
		latest[q] = i;
		return;
	}
	int c = s[i] >> k & 1;
	if (p) trie[q][c ^ 1] = trie[p][c ^ 1];
	trie[q][c] = ++tot;
	insert(i, k - 1, trie[p][c], trie[q][c]);
        // 这部分改成latest[q] = i,也能AC
	latest[q] = max(latest[trie[q][0]], latest[trie[q][1]]);
}

关于第六版322页 任务安排1 解法2的疑问

其中F[i]:=把前i个任务分成若干批执行的最小费用,但是比如有三个任务,在计算F[1]时,其中包含了S*(sumC[3] - sumC[0]),那F[i]其实不应该是前1个任务分成若干批执行的最小费用,因为它包含了后续的费用,因此这个dp的定义是否有问题?

P348 最优贸易不能用 Dijkstra

因为转移从 + 变成 min ,似乎不能满足用 Dijkstra 的性质(就像不能跑负权一样?)

比如下面这组数据,跑正图时 dis[4] 应该为 2 但是程序输出 4(但是最后答案好像还是对的?)

4 4
5 4 2 5
1 2 1
2 3 2
2 4 1
// Author:XuHt
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 100006, M = 1000006;
int n, m, tot = 0, Price[N];
int Head[N], Side[M], Next[M], ans[N];
int fHead[N], fSide[M], fNext[M], fans[N];
bool v[N], fv[N];
priority_queue<pair<int, int> > q;
priority_queue<pair<int, int> > fq;

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) scanf("%d", &Price[i]);
    for (int i = 1; i <= m; i++) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        Side[++tot] = y;
        Next[tot] = Head[x];
        Head[x] = tot;
        fSide[tot] = x;
        fNext[tot] = fHead[y];
        fHead[y] = tot;
        if (z == 2) {
            Side[++tot] = x;
            Next[tot] = Head[y];
            Head[y] = tot;
            fSide[tot] = y;
            fNext[tot] = fHead[x];
            fHead[x] = tot;
        }
    }
    memset(ans, 0x3f, sizeof(ans));
    memset(fans, 0xcf, sizeof(fans));
    ans[1] = Price[1];
    fans[n] = Price[n];
    q.push(make_pair(-ans[1], 1));
    fq.push(make_pair(fans[n], n));
    while (q.size()) {
        int x = q.top().second;
        q.pop();
        if (v[x]) continue;
        v[x] = 1;
        for (int i = Head[x]; i; i = Next[i]) {
            int y = Side[i];
            if (ans[y] > ans[x]) {
                ans[y] = ans[x];
                ans[y] = min(ans[y], Price[y]);
                q.push(make_pair(-ans[y], y));
            }
        }
    }
    for (int i = 1; i <= n; ++i) cout << ans[i] << ' '; // 就是这里
    cout << endl;
    while (fq.size()) {
        int x = fq.top().second;
        fq.pop();
        if (fv[x]) continue;
        fv[x] = 1;
        for (int i = fHead[x]; i; i = fNext[i]) {
            int y = fSide[i];
            if (fans[y] < fans[x]) {
                fans[y] = fans[x];
                fans[y] = max(fans[y], Price[y]);
                fq.push(make_pair(fans[y], y));
            }
        }
    }
    int Ans = 0;
    for (int i = 1; i <= n; i++) Ans = max(Ans, fans[i] - ans[i]);
    cout << Ans << endl;
    return 0;
}

0x54 树形 dp/背包类树形 dp/选课 std.cpp 一处有误

void dp(int x) {
	f[x][0] = 0;
	for (int i = 0; i < son[x].size(); i++) {
		int y = son[x][i];
		dp(y);
		for (int t = m; t >= 0; t--) 
			for (int j = 0; j <= t; j++)
                              f[x][t] = max(f[x][t], f[x][t-j] + f[y][j]);
			/* 或者
			for (int j = t; j >= 0; j--) // 经过实测,j 上界应为 m。树形结构的特殊性,y dp 过程已经结束,而 x 第二维度浅层次还未开始
				if (t + j <= m)
					f[x][t+j] = max(f[x][t+j], f[x][t] + f[y][j]);
                        */
	}
	if (x != 0)
		for(int t = m; t > 0; t--)
			f[x][t] = f[x][t-1] + s[x];
}

CH6703标程是否存在问题?

配套光盘/例题/0x60 图论/0x67 Tarjan算法与有向图连通性/PKU ACM Team's Excursion/CH6703.cpp

1
12 16 0 11 2
0 1 1000
0 2 1000
3 0 1000
1 3 1000
2 3 1000
3 4 4
4 5 1000
4 6 1000
5 7 1000
6 7 1000
7 8 6
8 9 1000
8 10 1000
8 11 1000
9 11 1000
10 11 1000

对于这组输入给出的解是-1

但该图存在0->1->3->4->5->7->8->11这一条路径,不应该输出-1

第128页排书的状态分支计算有误

第六版书上第 128 页最下面写道:

这个地方有两个小问题(不过不影响理解):

  1. 不应该是 <=,而应该是 >=;我理解作者的意思是不是要表达状态数就是不超过前面那个表达式除以 2
  2. 后面计算的结果应该是 561
    image

作者书写的很好,感谢作者👍。

关于第三章:数学部分 习题:大战朱自学的疑问

该题输入信息:ai, bi( bi <= ai <= 1200000),但是实际测试用例的输入数据很小,导致m[i]的乘积没有溢出,所以剩余定理可以直接算,但是如果数据量很大导致long long也会溢出的情况下,这道题该如何处理?

有符号整数溢出是 UB

在第 6 页里的代码片段:

long long mul(long long a, long long b, long long p) {
	a %= p, b %= p; // 当a,b一定在0~p之间时,此行不必要。
	long long c = (long double)a * b / p;
	long long ans = a * b - c * p;
	if (ans < 0) ans += p;
	else if (ans >= p) ans -= p;
	return ans;
}

a * b 可能导致有符号整数溢出,这是 UB,在 C++20 的文档第 74 页 6.8.1 Fundamental types 第二条有说明。

0x01位运算例题起床困难综合症

主函数中的贪心策略应该有问题,在测试中只能拿70分
应将
if(val+(1<<bit)<=m && res0<res1) val+=1<<bit,ans+=res1<<bit; else ans+=res0<<bit;
更改为
if(res0==1) ans+=res0<<bit; else { if(val+(1<<bit)<=m&&res1==1) val+=1<<bit,ans+=res1<<bit;

Contest Hunter 1813 数据是否有误?

第3个测试点第2组数据

对应数据输入:
6
2 6 3 1 5 4
数据输出:
a c c a b b d a a b b d

实际上,有一个更小的字典序的方案
a c c a b b a a d b b d

可能的原因:当S2栈顶元素可以弹出时,可以先不弹出,而先选择S1的入栈和出栈操作

没准是标程的问题

后面的数据应该也有类似问题

望及时修改

第三版错误

版本:2018年11月第3次印刷

0x13 P58
链表中的initialize和insert函数不应该返回int
insert()也可以改为
int insert(int p, int val) { q = ++tot; ... return q; }

0x6A P437
最大流的Edmonds-Karp增广路算法中的图一,从D到B的边流量应该是6

第二章 基本数据结构 例题回文串的最大长度代码有误

#include
#include
#include
#include
#define ull unsigned long long
using namespace std;
const int N = 1000006, P = 13331;
char s[N];
ull f1[N], f2[N], p[N];

ull H1(int i, int j) {
return (f1[j] - f1[i - 1] * p[j - i + 1]);
}

ull H2(int i, int j) {
return (f2[i] - f2[j + 1] * p[j - i + 1]);
}

int main() {
int id = 0;
p[0] = 1;
for (int i = 1; i < N; i++) p[i] = p[i - 1] * P;
while (scanf("%s", s + 1) && !(s[1] == 'E' && s[2] == 'N' && s[3] == 'D')) {
++id;
int ans = 0, len = strlen(s + 1);
f2[len+1] = 0;
for (int i = 1; i <= len; i++) f1[i] = f1[i - 1] * P + s[i];
for (int i = len; i; i--) f2[i] = f2[i + 1] * P + s[i];
for (int i = 1; i <= len; i++) {
// l初始化1有问题
// l需要从0开始,因为可能一开始l就小于r,类似这样的用例abcdefghijklmn,ans在i=1时会直接变成3
// int l = 1, r = min(i - 1, len - i);
int l = 0, r = min(i - 1, len - i);
while (l < r) {
int mid = (l + r + 1) >> 1;
if (H1(i - mid, i - 1) == H2(i + 1, i + mid)) l = mid;
else r = mid - 1;
}
ans = max(l * 2 + 1, ans);
// l = 1, r = min(i - 1, len - i + 1);
l = 0, r = min(i - 1, len - i + 1);
while (l < r) {
int mid = (l + r + 1) >> 1;
if (H1(i - mid, i - 1) == H2(i, i + mid - 1)) l = mid;
else r = mid - 1;
}
ans = max(l * 2, ans);
}
printf("Case %d: %d\n", id, ans);
}
return 0;
}

POJ2248 “Addition Chains”标程的问题

@WFZWF 提供的标程默认最短Addition Chain满足性质X[k]=X[k-1]+X[i]其中1≤i≤k-1,但这个性质不总是正确的。首个反例是当n=12509时,最短Addition Chain长度为18(1 2 4 8 16 17 32 64 128 256 512 1024 1041 2082 4164 8328 12492 12509),用标程中的算法将会得到长度为19的错误结果(1 2 4 8 16 32 64 128 256 512 1024 2048 4096 4160 4168 8336 12504 12508 12509).
尽管在本题的数据范围下此算法不会得到错误结果,且能够缩短运行时间,但是作为标程可能会对读者产生误导。

疑问:关于 0x63 节的两次 BFS 求树的直径

是否应当加上一句话?

在树上有负边权的时候不能使用两次BFS/DFS求树的直径。

感觉如果不加会误导一些没去自己证明的同学(

例题当中的 “巡逻” 一题就需要注意这一点,或许可以在这道题目下添加说明?

0x66 tarjan_dcc_euler.cpp描述问题

第82行起

// tarjan算法求无向图的割点、点双连通分量并缩点

root变量没有定义,c数组变量没有定义
第149行

		printf("e-DCC #%d:", i);

应该是

		printf("v-DCC #%d:", i);

这行描述在书上也是这样的

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.