标记幺半群左作用在信息幺半群上的代数结构
标记幺半群 \(T\) 左作用在信息幺半群 \(S\) 上的代数结构: \((T, \times, 1_{T}, S, +, 0_{S}, \circ)\) 。
对于信息幺半群 \((S, +, 0_{S})\) ,满足:
- 封闭性:\(\forall x, y \in S\) ,\(x + y \in S\) 。
- 结合律:\(\forall x, y, z \in S\) ,\((x + y) + z = x + (y + z)\) 。
- 存在单位元:\(\forall x \in S, \exists 0_{S} \in S\) ,\(x + 0_{S} = 0_{S} + x\) 。
对于标记幺半群 \((T, \times, 1_{T})\) ,满足:
- 封闭性:\(\forall a, b \in T\) ,\(a \times b \in T\) 。
- 结合律:\(\forall a, b, z \in S\) ,\((a \times b) \times c = a \times (b \times c)\) 。
- 存在单位元:\(\forall a \in T, \exists 1_{T} \in T\) ,\(a \times 1_{T} = 1_{T} \times a\) 。
对于 \(T\) 左作用在 \(S\) ,满足:
\[T \circ S : T \times S \rightarrow S
\]
以下是 \((T, \times, 1_{T}, S, +, 0, \circ)\) 框架:
有时候传入数组的地址: const int a[] 可以考虑换成浅拷贝 vector :const std::vector<int> &a 。
view
struct S {
i64 sum, sz;
S(){}
S(i64 _sum, i64 _sz) : sum(_sum), sz(_sz) {}
void init(int x, int a[]) { *this = S(a[x], 1); }
};
struct T {
i64 mul, add;
T() : mul(1), add(0){}
T(i64 _mul, i64 _add) : mul(_mul), add(_add){}
} I;
S operator + (const S &LHS, const S &RHS) {
S ret;
ret = { ( LHS.sum + RHS.sum ) % MOD, LHS.sz + RHS.sz };
return ret;
}
T operator * (const T &LHS, const T &RHS) {
T ret;
ret = { ( LHS.mul * RHS.mul ) % MOD, ( LHS.add * RHS.mul + RHS.add ) % MOD };
return ret;
}
S operator >> (const T &t, const S &f) {
S ret;
ret = { ( ( f.sum * t.mul ) % MOD + ( f.sz * t.add ) % MOD ) % MOD, f.sz };
return ret;
}
以下是线段树模板,基本无需修改,但初始化信息时需要传入数组地址,默认名字是 a 。
view
##define ls (id << 1)
#define rs (id << 1 | 1)
struct Node {
S f;
T t;
} seg[MAXN * 4];
void upd(int id) {
seg[id].f = seg[ls].f + seg[rs].f;
}
void psd(int id) { // 这里一般要看这个节点是不是空节点,这个代码有时可能存在问题
void setT(int, T);
setT(ls, seg[id].t );
setT(rs, seg[id].t );
seg[id].t = I;
}
void setT(int id, T t) {
seg[id].f = t >> seg[id].f;
seg[id].t = seg[id].t * t;
}
void build(int id, int l, int r) {
if (l == r) {
seg[id].f.init(l, a);
return;
}
int mid = ( l + r ) >> 1;
build(ls, l, mid );
build(rs, mid + 1, r );
upd(id);
}
S ask(int id, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return seg[id].f;
}
psd(id);
int mid = ( l + r ) >> 1;
if ( qr <= mid ) return ask(ls, l, mid, ql, qr );
else if ( ql > mid ) return ask(rs, mid + 1, r, ql, qr );
else return ask(ls, l, mid, ql, mid ) + ask(rs, mid + 1, r, mid + 1, qr);
}
void edt(int id, int l, int r, int ql, int qr, T t) {
if (l == ql && r == qr) {
setT( id, t );
return;
}
psd(id);
int mid = ( l + r ) >> 1;
if ( qr <= mid ) edt(ls, l, mid, ql, qr, t );
else if ( ql > mid) edt(rs, mid + 1, r, ql, qr, t );
else edt(ls, l, mid, ql, mid, t ), edt(rs, mid + 1, r, mid + 1, qr, t );
upd(id);
}
——永远是挑战而不是练习,下次一定更好。
浙公网安备 33010602011771号