先两边取最下,最左。然后讨论哪条边更优。别忘了n->1的那一条边也要加入。
做完之后可以整个再做一次凸包。注意凸包的三点贡献情况,cmp要加入距离。
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 a≤c,ans+=cnt[c],cnt[c]++。c是该四元环入度为2的最小的点(至多两个入度为2)。
核心操作:
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);
}
求组合数对 p k ≤ 1 0 7 p^k\leq 10^7 pk≤107取模,与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/p⋅pre[pk]n/pk⋅pre[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;
}
}
公式见https://blog.csdn.net/dt_kang/article/details/88805837
对于一个集合S,max可以被解释为前边有0个比他大的数。
那么基本的minmax容斥就相当于枚举某个数与比他大的数的子集,就相当于枚举一个整体的子集,然后系数就是 ( − 1 ) ∣ T ∣ − 1 min T (-1)^{|T|-1}\min{T} (−1)∣T∣−1minT。
对于他的扩展形式也有组合解释。第k大的就相当于前面有 k − 1 k-1 k−1个比他大。
那么定义 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=k∑∣S∣−1Fi(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=k∑∣S∣−1Gi(ki)(−1)i−k
要求 max k S \max_kS maxkS,即为求 F k − 1 F_{k-1} Fk−1。形象地说,就是取一个 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 (k−1∣T∣−1)(−1)∣T∣−1−(k−1)minT
很容易打错,关键点:
常数很大,空间顶着开。
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;
}
注意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)=∑p∣n,q∣m[(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);
}
}