错误合集2

闵可夫斯基和

先两边取最下,最左。然后讨论哪条边更优。别忘了n->1的那一条边也要加入。
做完之后可以整个再做一次凸包。注意凸包的三点贡献情况,cmp要加入距离。

爆零警告2

  1. 应故意检查pai.cpp的正确性。WA之后有没有记得return啊?
  2. 暴力与std如非必要不公用代码。若迫切需要应多次检查公用部分。
  3. 测空间,测空间,测空间
  4. gen.cpp有没有srand啊?
  5. 测极限
  6. 写部分分的时候,注意部分分之间的包含关系!!!
  7. 写部分分的时候,注意部分分之间的包含关系!!!
  8. 写部分分的时候,注意部分分之间的包含关系!!!
  9. 写部分分的时候,注意部分分之间的包含关系!!!

回文树

void init() {
    tot = 1;
    len[0] = -1, len[1] = 0;
    fail[1] = 0;
    las = 1;
}
void extend(int loc) {
    char r = s[loc] - 'a';
    while (s[loc] != s[loc - len[las] - 1]) las = fail[las];
    if (c[las][r] == 0) {
        ++tot;
        L[tot] = loc;
        len[tot] = len[las] + 2;
        if (len[tot] == 1)
            fail[tot] = 1;
        else {
            int k = fail[las];
            while (s[loc] != s[loc - len[k] - 1]) {
                k = fail[k];
            }
            // assert(c[k][r] != 0);
            fail[tot] = c[k][r];
        }
        c[las][r] = tot;
    }
    las = c[las][r];
}

三元环复杂度

无向图:度数大向度数小连,枚举边 x − > y x->y x>y,再枚举边 y − > z y->z y>z检查是否有 x − > z x->z x>z
复杂度:比点x度数大并且与x相邻的点至多 m \sqrt m m 个。
有向图:视作无向图,再判断边是否存在
其实可以直接 O ( n 3 / w ) O(n^3/w) O(n3/w)只要空间开的下。

四元环:复杂度一样。先枚举原图边(a,b),然后定向边(b,c),需要保证 a ≤ c a\leq c ac,ans+=cnt[c],cnt[c]++。c是该四元环入度为2的最小的点(至多两个入度为2)。

爆零警告

  1. vector clear之后再访问越界下标是不会RE的,但是会返回奇怪的数!
  2. 写部分分的时候,注意部分分之间的包含关系!!!

吉司机线段树

  1. 维护最值与严格次最值。维护对最值操作标记与全局操作标记。
  2. 若当前操作可以将最值与次最值合并,直接往下走。
  3. 对最值取max可以变成对最值加减。
  4. 下传对最值操作标记时,只能往有最值的儿子传。由于下传标记时子树内未被访问过,因此去掉所有打在当前点的标记后原先的最值就是儿子当前存的最值。

核心操作:

void down(int x) {
	int xmi = mi[x] - mitag[x] - adtag[x];
	if (tmi[x]) {
		if (mi[x<<1]==xmi) tmi[x<<1] += tmi[x];
		if (mi[x<<1|1]==xmi) tmi[x<<1|1] += tmi[x];
		tmi[x] = 0;
	}
	if (t[x]) {
		t[x<<1] += t[x];
		t[x<<1|1] += t[x];
		t[x] = 0;
	}
	
	if (mitag[x]) {
		if (mi[x<<1]==xmi) put_mi_tag(x<<1,mitag[x]);
		if (mi[x<<1|1]==xmi) put_mi_tag(x<<1|1,mitag[x]);
		mitag[x]=0;
	}
	
	if (adtag[x]){
		put_tag(x<<1,adtag[x]);
		put_tag(x<<1|1,adtag[x]);
		adtag[x]=0;
	}
}

void qmax(int x, int l, int r, int tl, int tr, int v) {
	if (l > tr || r < tl) return;
	if (tl <= l && r <= tr) {
		if (mi[x] >= v) return;
		if (cmi[x] > v) { //此处无等号!
			put_mi_tag(x, v - mi[x]);
			tmi[x]++;
		} else {
			down(x);
			qmax(x << 1, l, mid, tl, tr, v);
			qmax(x << 1 | 1, mid + 1, r, tl, tr, v);
			upd(x);
		}
		return;
	}
	down(x);
	qmax(x << 1, l, mid, tl, tr, v);
	qmax(x << 1 | 1, mid + 1, r, tl, tr, v);
	upd(x);
}

扩展Lucas

求组合数对 p k ≤ 1 0 7 p^k\leq 10^7 pk107取模,与CRT联合使用。
具体的做法是将 n ! n! n!表示为 p a × b p^a\times b pa×b,其中b与 p p p互质。
关键的式子是 n ! = ( n p ) ! ⋅ p n / p ⋅ p r e [ p k ] n / p k ⋅ p r e [ n % p k ] n!=(\frac np)!\cdot p^{n/p}\cdot pre[p^k]^{n/p^k}\cdot pre[n\%p^k] n!=(pn)!pn/ppre[pk]n/pkpre[n%pk]
形象地说,就是将n以内的p的倍数与其余数分开算。

const int Z = 2e6;
struct exlucas{
	int p, k, pk, phi;
	ll pre[Z]; //without any p|n
	
	ll mtl(pii a, pii b) {
		if (a.first + b.first > k) return 0;
		return a.second * b.second % pk * ksm(p, a.first + b.first) % pk;
	}
	
	pii mul(pii a, pii b) {
		a.first += b.first;
		a.second = a.second * b.second % pk;
		return a;
	}
	
	void mul(pii &a, ll b) {
		a.second = a.second * b % pk;
	}
	
	ll ksm(ll x, ll y) {
		if (x == -1 || x == pk - 1) return y & 1 ? pk - 1 : 1;
		if (x == 1) return 1;
		ll ret = 1; for (; y; y >>= 1) {
			if (y & 1) ret = ret * x % pk;
			x = x * x % pk;
		}
		return ret;
	}
	
	pii jc(ll x) {
		if (x == 0) return pii(0, 1);
		pii z = jc(x / p);
		z.first += x / p;
		mul(z, ksm(pre[pk], x / pk) * pre[x % pk] % pk);
		return z;
	}
	
	ll C(ll n, ll m) {
		pii z = jc(n - m);
		pii a = mul(jc(n - m), jc(m));
		a.first = -a.first, a.second = ksm(a.second, phi - 1);
		return mtl(jc(n), a);
	}
	
	void init(int _p, int _k) {
		p = _p, k = _k;
		pk = no_mod_ksm(p, k);
		phi = no_mod_ksm(p, k - 1) * (p - 1);
		pre[0] = 1;
		for(int i = 1; i <= pk; i++) {
			int t = i; if (t % p == 0) t = 1;
			pre[i] = pre[i - 1] * t % pk;
		}
	}
	
	pii inv(pii z) {
		z.first = -z.first, z.second = ksm(z.second, phi - 1);
		return z;
	}
	
	ll toll(pii z) {
		return ksm(p, z.first) * z.second % pk;
	}
	
}

min-max类容斥

公式见https://blog.csdn.net/dt_kang/article/details/88805837
对于一个集合S,max可以被解释为前边有0个比他大的数。
那么基本的minmax容斥就相当于枚举某个数与比他大的数的子集,就相当于枚举一个整体的子集,然后系数就是 ( − 1 ) ∣ T ∣ − 1 min ⁡ T (-1)^{|T|-1}\min{T} (1)T1minT

对于他的扩展形式也有组合解释。第k大的就相当于前面有 k − 1 k-1 k1个比他大。
那么定义 F k F_k Fk表示前面恰好有 k k k个数比他大的数, G k G_k Gk表示前面“至少”有k个数比他大的数之和。那么有 G k = ∑ i = k ∣ S ∣ − 1 F i ( i k ) G_k=\sum_{i=k}^{|S|-1}F_i\binom{i}{k} Gk=i=kS1Fi(ki)二项式反演,就有 F k = ∑ i = k ∣ S ∣ − 1 G i ( i k ) ( − 1 ) i − k F_k=\sum_{i=k}^{|S|-1}G_i\binom{i}{k}(-1)^{i-k} Fk=i=kS1Gi(ki)(1)ik
要求 max ⁡ k S \max_kS maxkS,即为求 F k − 1 F_{k-1} Fk1。形象地说,就是取一个 S S S的子集 T T T,系数就是 ( ∣ T ∣ − 1 k − 1 ) ( − 1 ) ∣ T ∣ − 1 − ( k − 1 ) min ⁡ T \binom{|T|-1}{k-1}(-1)^{|T|-1-(k-1)}\min T (k1T1)(1)T1(k1)minT

可持久化区间修改

很容易打错,关键点:

  1. 设change函数,传入旧根,返回新根
  2. 所有修改都要开新点,包括down的时候。

常数很大,空间顶着开。

int newnode(int ori) {
	++tot;
	lc[tot] = lc[ori];
	rc[tot] = rc[ori];
	tag[tot] = ori == 0 ? -1 : tag[ori];
	return tot;
}

void down(int x) {
	if (tag[x] == -1) return;
	lc[x] = newnode(lc[x]);
	rc[x] = newnode(rc[x]);
	tag[lc[x]] = tag[rc[x]] = tag[x];
	tag[x] = -1;
}

int change(int old, int l, int r, int tl, int tr, int v) {
	if (tl > r || tr < l) return old;
	int x = newnode(old);
	if (tl <= l && r <= tr) {
		tag[x] = v;
		return x;
	}
	down(x);
	lc[x] = change(lc[x], l, mid, tl, tr, v);
	rc[x] = change(rc[x], mid + 1, r, tl, tr, v);
	return x;
}

半平面交

考前背板,注意画图理解。
注意判断队列中直线<=2的情况

bool in(line z, pot x) {
	return z.vec * (x - z.ori) > -eps;
}
db half_plane_intersection() {
	for(int i = 1; i <= tot; i++)
		ls[i].at2 = atan2(ls[i].vec.y, ls[i].vec.x);
	sort(ls + 1, ls + 1 + tot, cmp);
	int rt = 0;
	for(int i = 1; i <= tot; i++) {
		if (rt != 0 && fabs(rs[rt].at2 - ls[i].at2) < eps) {
			if (rs[rt].vec * (ls[i].ori - rs[rt].ori) > eps) {
				rs[rt] = ls[i];
			}
		} else rs[++rt] = ls[i];
	}
	memcpy(ls,rs,sizeof rs); tot = rt;
	static line Q[N * 2]; int h = 1, t = 0;
	for(int i = 1; i <= tot; i++) {
		while (h < t && !in(ls[i], inse(Q[t - 1], Q[t])))t--;
		while (h < t && !in(ls[i], inse(Q[h], Q[h + 1])))h++;
		Q[++t] = ls[i];
	}
	while (h < t && !in(Q[h], inse(Q[t], Q[t - 1])))t--;
	while (h < t && !in(Q[t], inse(Q[h], Q[h + 1])))h++;
	db S = 0;
	pot o = inse(Q[h], Q[h + 1]);
	Q[t + 1] = Q[h];
	if (t - h + 1 <= 2) return 0;
	for(int i = h + 1; i < t; i++) {
		S += (inse(Q[i], Q[i + 1]) - o) * (inse(Q[i + 1], Q[i + 2]) - o);
	}
	return S * 0.5;
}

多组数据的输出问题

  1. 多个出口(比如说有无解的情况,中途输出-1时)时,注意:换行,continue
  2. 尽量尝试多种情况。

可持久化非旋treap

注意merge的地方的随机要基于子树大小,否则常数会爆炸。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned int ll;
const int N = 1e5 + 10, C = 200 * N;
int m, tot, sz[C], c[C][2], val[C], rv[C];
char opt[10];
typedef pair<int,int> pii;

int newnode(int x = 0) {
	int y = ++tot;
	sz[y] = x == 0 ? 1 : sz[x];
	c[y][0] = c[x][0], c[y][1] = c[x][1];
	val[y] = val[x];
	rv[y] = rv[x];
	return y;
}

void upd(int x) {sz[x] = sz[c[x][0]] + sz[c[x][1]] + 1;}
void putag(int &x) {
	if (x == 0) return;
	x = newnode(x);
	swap(c[x][0], c[x][1]);
	rv[x] ^= 1;
}

void down(int x) {
	if (rv[x]) {
		putag(c[x][0]);
		putag(c[x][1]);
		rv[x] = 0;
	}
}

pii split(int x, int k) {
	if (k == 0) return pii(0, x);
	down(x);
	int z = newnode(x);
	pii a;
	if (sz[c[z][0]] >= k) {
		a = split(c[z][0], k);
		c[z][0] = a.second;
		a.second = z;
	} else {
		a = split(c[z][1], k - 1 - sz[c[z][0]]);
		c[z][1] = a.first;
		a.first = z;
	}
	upd(z);
	return a;
}

int mer(int x, int y) {
	if (x == 0 || y == 0) return x + y;
	down(x), down(y);
	if (rand() % (sz[x] + sz[y]) <= sz[x]) {
		int z = newnode(x);
		c[z][1] = mer(c[z][1], y);
		upd(z);
		return z;
	} else {
		int z = newnode(y);
		c[z][0] = mer(x, c[z][0]);
		upd(z);
		return z;
	}
}

int rt;
int main() {
	freopen("editor.in","r",stdin);
	freopen("editor.out","w",stdout);
	cin >> m;
	for(int e = 1; e <= m; e++) {
		scanf("%s",opt);
		if(opt[0]=='I'){
			int x; char c;
			scanf("%d %c",&x,&c);
			pii a = split(rt, x);
			int z = newnode(); val[z] = c;
			rt = mer(a.first, z);
			rt = mer(rt, a.second);
		}
		if (opt[0] == 'D') {
			int l, r; scanf("%d %d", &l, &r);
			pii a = split(rt, l - 1);
			pii b = split(a.second, r - l + 1);
			rt = mer(a.first, b.second);
		}
		if (opt[0] == 'C') {
			int l, r, x; scanf("%d %d %d", &l, &r, &x);
			pii a = split(rt, l - 1);
			pii b = split(a.second, r - l + 1);
			a = split(rt, x);
			rt = mer(mer(a.first, b.first), a.second);
		}
		if (opt[0] == 'R') {
			int l, r; scanf("%d %d", &l, &r);
			pii a = split(rt, l - 1);
			pii b = split(a.second, r - l + 1);
			putag(b.first);
			rt = mer(mer(a.first, b.first), b.second);
		}
		if (opt[0] == 'Q') {
			int x; scanf("%d", &x);
			pii a = split(rt, x - 1);
			pii b = split(a.second, 1);
			putchar(val[b.first]);
		}
	}
	cerr << tot << endl;
}

约数个数函数的合并

众所周知,约数个数函数g是积性函数
对于不互质的n,m,我们也有 g ( n m ) = ∑ p ∣ n , q ∣ m [ ( p , q ) = 1 ] g(nm)=\sum_{p|n,q|m}[(p,q)=1] g(nm)=pn,qm[(p,q)=1]

浮点数运算

错误写法:sqrt(n),正确写法:sqrt(n+eps)
实数二分直接枚举二分次数,并且不加eps。

调试技巧

-fsanitize=address
检查数组越界
-ftrapv
检查整型越界

求欧拉回路

final[x]维护了下一条没有被删除的边,这样不会有时间问题。
dfs结束时加入x,可以实现嵌套环的功能。

void dfs(int x, int fe) {
	while (final[x]) {
		if (!ban[final[x]]) {
			int i = final[x];
			ban[i] = ban[i ^ 1] = 1;
			final[x] = nex[i];
			dfs(to[i], i);
		} else
			final[x] = nex[final[x]];
	}
	if (fe != 0) {
		fe ^= 1;
		if (dir[fe] == 0) seq[++len]=(no[fe] * 2 - 1), seq[++len]=(no[fe] * 2);
		else seq[++len]=(no[fe] * 2), seq[++len]=(no[fe] * 2 - 1);
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值