错误合集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 
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);
	}
}

你可能感兴趣的:(错误合集2)