模板库
不定期更新,以下部分模板和他人共同完成,已标明
目录
就是提醒你看一下目录。
珂朵莉树
template<typename Tp, typename comp = std::equal_to<Tp> >
/**
* comp(a,b) to decide if they can be merged
* std::equal_to<_Tp> comes from <functional> (stl_function.h)
*
*/
class Chtholly {
public:
class iterator;//declare iterator
private:
struct node {//nodes in the set
const int l;
mutable int r;
mutable Tp val;
node() = delete;//for safety
node(int L) : l(L) { }//for convenience
node(int L, int R, const Tp& V) : l(L), r(R), val(V) { }
bool operator < (const node& y) const {//for set's comparison
return l < y.l;
}
};
std::set<node> st;
typedef typename std::set<node>::iterator IT;
int _size_n;
iterator _begin_iterator, _end_iterator;
IT split(int pos) {
if (pos<1) return st.begin();//A programmer get into a bar...
IT it(std::prev(st.upper_bound(pos)));
if (it->r < pos) return st.end();//Another programmer get into the bar...
if (it->l == pos) return it;
int tmpr = it->r;
it->r = pos - 1;
return st.insert(node(pos, tmpr, it->val)).first;
}
IT merge(IT i) {
IT l(i), r(i);//caution: [l,r)
while (l != st.begin()
&& comp()(prev(l)->val, i->val)
) --l;
while (r != st.end()
&& comp()(r->val, i->val)
) ++r;
int tl(l->l), tr(prev(r)->r);
Tp tv(i->val);
if (tl>tr) return i;//
st.erase(l, r);
return st.insert(node(tl, tr, tv)).first;
}
void merge(IT l, IT r) {//[l,r]
int ed(r->r);
while (l->r < ed) l=next(merge(l));
}
void merge()
{ merge(st.begin(), --st.end()); }
public:
Chtholly()//an empty sequence
: st(), _size_n(0),
_begin_iterator(1, st.begin()), _end_iterator(_size_n+1, st.end())
{ }
Chtholly(int n, const Tp& val)
: st(), _size_n(n)
{
st.insert(node(1, n, val));
_begin_iterator = iterator(1, st.begin());
_end_iterator = iterator(_size_n+1, st.end());
}
template<typename FI>
Chtholly(FI first, FI end)//copy value from [first,end)
: st(), _size_n(0)
{
if (first == end) return;
Tp val(*first++);
int l(1), r(1);
while (first != end) {
if ( !comp()(*first, val) ) {
++_size_n;
st.insert(node(l, r, val));
val = *first;
l = r + 1;
}
++first, ++r;
}
++_size_n;
st.insert(node(l, r, val));
_begin_iterator = iterator(1, st.begin());
_end_iterator = iterator(_size_n+1, st.end());
}
void clear()
{ st.clear(); }
void rebuild()
{ *this = Chtholly(); }
void rebuild(int n, const Tp& val)
{ *this = Chtholly(n, val); }
template<typename FI>
void rebuild(FI first, FI end)
{ *this = Chtholly(first, end); }
void assign(int l, int r, const Tp& val) {//force to remake [l,r] val
if (l>r) return;
IT itr(split(r+1)), itl(split(l));
st.erase(itl, itr);
IT i = st.insert(node(l, r, val)).first;
merge(i);
}
template<typename Rtn, typename... ArgList>
void modify(int l , int r, Rtn (*func)(iterator, ArgList...), ArgList... arg) {
if (l>r) return;//The third programmer ask for a bunch of noodles...
IT itr(split(r+1)), itl(split(l));
for (IT it(itl); it!=itr; ++it) {
func(iterator(it->l, it), arg...);
}
merge(itl, itr);
}
template<typename FI>//copy [l,r] to 'destin'
void copy_to(FI destin, int l, int r) const {
IT it(--st.upper_bound(l));
while (it != st.end()) {
int lim(min(it->r, r));
while (l <= lim) *destin++=it->val, ++l;
if (l>r) return;
++it;
}
}
const Tp operator [] (const int pos) const//you shound not change its value
{ return std::prev(st.upper_bound(pos))->val; }
//O(log n)
int size() const//length of the real array
{ return _size_n; }
iterator begin() const
{ return _begin_iterator; }
iterator end() const
{ return _end_iterator; }
int bsize() const
{ return st.size(); }
class iterator {
private:
int pos;//iterator in the real array
IT it;//iterator in set
public:
iterator() = default;//for safety
iterator(int ind, const IT& i)
: pos(ind), it(i) { }
iterator operator ++ () {
if (++pos > it->r) ++it;
return *this;
}
iterator operator ++ (int) {
iterator tmp = *this;
if (++pos > it->r) ++it;
return tmp;
}
iterator operator -- () {
if (--pos < it->l) --it;
return *this;
}
iterator operator -- (int) {
iterator tmp = *this;
if (--pos < it->l) --it;
return tmp;
}
bool operator == (const iterator& _x) const { return pos == _x.pos; }
bool operator != (const iterator& _x) const { return pos != _x.pos; }
bool operator < (const iterator& _x) const { return pos < _x.pos; }
bool operator > (const iterator& _x) const { return pos > _x.pos; }
bool operator <= (const iterator& _x) const { return pos <= _x.pos; }
bool operator >= (const iterator& _x) const { return pos >= _x.pos; }
Tp& operator * () { return it->val; }
const int left() const { return it->l; }
const int right() const { return it->r; }
iterator prev() const { return iterator(it->l - 1, std::prev(it)); }
iterator next() const { return iterator(it->r + 1, std::next(it)); }
};
};
from Gyan(stO 封装の神 Orz)
压行版见https://gyan083.blog.luogu.org/da-bao-yi-ge-ke-duo-li-post
食用指南:
定义时传入第1个参数表示珂朵莉树维护的信息的类型,第2个参数表示两个节点在何时需要被合并(可以不写,默认为两个节点信息完全相同)
初始化时不传参数默认为空,可以传大小和初始值(只能有一个),还可以从一个数组或stl容器中一一拷贝值(似乎要\(\Theta(n\log n)\),效率略低)
支持调用子类Chtholly::iterator来访问具体的节点信息:
left()/right()询问区间的左右边界
可以用++/--移动迭代器,也可以用prev(it)/next(it)访问与it相邻的迭代器
Chtholly树支持:
begin()/end()返回第一/最后的迭代器
rebuild(int,Tp&)第一个参数设置珂朵莉树维护的区间大小,第二个参数将这个区间赋初值(也可以写在构造函数里,但不常用)
bsize()查询当前树有多少个节点,size()查看当前维护区间的大小
[]支持随机访问一个节点所在的区间,越界则返回最后一个节点,复杂度是\(\Theta(\log bsize)\)的
clear()可以清空树从来没有用过
modify()需要传入修改操作在原序列上在左右边界和修改规则对应的函数change(第一个参数必须是Chtholly::iterator,之后可以接任意长参数),之后写change需要的其他参数
Splay
template<typename _Tp,unsigned N=10,_Tp INF=_Tp(2147483647)>
class Splay
{
private:
unsigned Root,node_cnt;
struct Node
{
_Tp v;
unsigned cnt,siz,fa,ch[2];
Node()=default;
};
std::vector<Node> node;
std::vector<unsigned> del_list;
unsigned vir_alloc()//动态申请点
{
if(!del_list.empty())
{
unsigned tmp=del_list.back();
del_list.pop_back();
return tmp;
}
++node_cnt;
if(node_cnt>=node.size())
{
if(node_cnt==node.capacity()) node.reserve(node.capacity()+15);
node.emplace_back(Node());
}
return node_cnt;
}
void update(unsigned x)//更新
{
node[x].siz=node[node[x].ch[0]].siz+node[node[x].ch[1]].siz+node[x].cnt;
}
void rotate(unsigned x)//旋转
{
unsigned y=node[x].fa,z=node[y].fa,k=(node[y].ch[1]==x);
node[z].ch[node[z].ch[1]==y]=x;
node[x].fa=z;
node[y].ch[k]=node[x].ch[k^1];
node[node[x].ch[k^1]].fa=y;
node[x].ch[k^1]=y;
node[y].fa=x;
update(y),update(x);
}
void splay(unsigned x,unsigned target=0)//转到目标节点的儿子
{
while(node[x].fa!=target)
{
unsigned y=node[x].fa,z=node[y].fa;
if(z!=target)
((node[z].ch[0]==y)^(node[y].ch[0]==x))?rotate(x):rotate(y);
rotate(x);
}
if(!target)Root=x;
}
void find(_Tp x)//对应值节点转到根
{
unsigned cur=Root;
if(!cur)return;
while(node[cur].ch[x>node[cur].v]&&x!=node[cur].v)
cur=node[cur].ch[x>node[cur].v];
splay(cur);
}
unsigned find_pre_id(_Tp x)//查前驱编号
{
find(x);
if(node[Root].v<x)return Root;
unsigned cur=node[Root].ch[0];
while(node[cur].ch[1]) cur=node[cur].ch[1];
splay(cur);
return cur;
}
unsigned find_nxt_id(_Tp x)//查后继编号
{
find(x);
if(node[Root].v>x)return Root;
unsigned cur=node[Root].ch[1];
while(node[cur].ch[0]) cur=node[cur].ch[0];
splay(cur);
return cur;
}
public:
Splay()//初始化
{
Root=node_cnt=0;
node.resize(N);
node[0].siz=0,node[0].cnt=0,node[0].fa=0;
insert(INF),insert(-INF);
}
void insert(_Tp x)//插入
{
unsigned cur=Root,from=0;
while(cur&&x!=node[cur].v)
from=cur,cur=node[cur].ch[x>node[cur].v];
if(cur)
++node[cur].cnt;
else
{
cur=vir_alloc();
if(!from) Root=cur;
else node[from].ch[x>node[from].v]=cur;
node[cur].v=x;
node[cur].cnt=1;
node[cur].fa=from;
node[cur].siz=1;
node[cur].ch[0]=node[cur].ch[1]=0;
}
splay(cur);
}
_Tp get_pre(_Tp x)//查前驱值
{
return node[find_pre_id(x)].v;
}
_Tp get_nxt(_Tp x)//查后继值
{
return node[find_nxt_id(x)].v;
}
bool erase(_Tp x)//删除
{
unsigned x_pre=find_pre_id(x),x_nxt=find_nxt_id(x);
splay(x_pre);
splay(x_nxt,x_pre);
unsigned cur=node[x_nxt].ch[0];
if(!cur)return 0;
if(node[cur].cnt>1)
{
--node[cur].cnt;
splay(cur);
}
else del_list.emplace_back(node[x_nxt].ch[0]),node[x_nxt].ch[0]=0;
return 1;
}
_Tp get_kth(unsigned rank)//找排名为k的
{
++rank;
unsigned cur=Root,son;
if(node[cur].siz<rank) return INF;
while(1)
{
son=node[cur].ch[0];
if(rank>node[son].siz+node[cur].cnt)
{
rank-=node[son].siz+node[cur].cnt;
cur=node[cur].ch[1];
}
else if(node[son].siz>=rank) cur=son;
else return splay(cur),node[cur].v;
}
}
unsigned get_rank(_Tp x)//查排名
{
find(x);
if(node[Root].v>=x) return node[node[Root].ch[0]].siz;
return node[node[Root].ch[0]].siz+node[Root].cnt;
}
void reserve(unsigned cap)//直接申请更大空间
{
if(node.capacity()<cap) node.reserve(cap);
if(del_list.capacity()<(cap>>1)) del_list.reserve(cap>>1);
}
};
from 人形魔芋
具体的食用指南可以在https://www.cnblogs.com/cmy-blog/p/splay.html 上看
使用了动态开点,效率较低,如果不是树套树等大小不定的场合,建议设定初始大小
LCT
class Data
{
public:
Data()=default;
};
template<int N=114514>
class LCT
{
public: Data d[maxn];
private:
class node
{
public:
bool rev;
int c[2],fa;
node()=default;
};
node s[N];
#define fa(x) s[x].fa
#define lc(x) s[x].c[0]
#define rc(x) s[x].c[1]
#define sonTp(x,fa) (s[fa].c[1]==x)
inline void pushup(int x)
{x--;}
inline void access_pushup(int x,int y)
{x&=y;}
inline void cut_pushup(int x,int y)
{x&=y;}
inline void link_pushup(int x,int y)
{x&=y;}
inline bool nroot(int x) {return s[fa(x)].c[0]==x||s[fa(x)].c[1]==x;}
inline void rev(int x)
{
if(!x) return;
swap(lc(x),rc(x)),s[x].rev^=1;
pushup(x);
}
inline void pushdown(int x)
{
if(s[x].rev)
rev(lc(x)),
rev(rc(x)),
s[x].rev=0;
}//记得这里也是要改的
inline void push_all(int x)
{
if(nroot(x)) push_all(fa(x));
pushdown(x);
}
inline void rotate(int x)
{
int y=fa(x),z=fa(y),k=sonTp(x,y),w=s[x].c[!k];
if(nroot(y)) s[z].c[sonTp(y,z)]=x;
s[x].c[!k]=y;
s[y].c[ k]=w;
if(w) fa(w)=y;
fa(x)=z,fa(y)=x;
pushup(y);
pushup(x);
}
inline void splay(int x)
{
static int y,z;
push_all(x);
while(nroot(x))
{
y=fa(x),z=fa(y);
if(nroot(y)) rotate(sonTp(x,y)!=sonTp(y,z)?x:y);
rotate(x);
}
pushup(x);
}
public:
inline void access(int x)
{
for(int y=0;x;x=fa(y=x))
splay(x),
access_pushup(x,y),
rc(x)=y,
pushup(x);
}
private:
inline void softRoot(int x)
{
access(x),
splay(x);
}
inline int findRoot(int x)
{
softRoot(x);
for(;lc(x);pushdown(x),x=lc(x));
splay(x);
return x;
}
public:
inline bool sameRoot(int x,int y) {return findRoot(x)==findRoot(y);}
inline void makeRoot(int x)
{
softRoot(x),
rev(x);
}
inline void split(int x,int y)
{
makeRoot(x),
softRoot(y);
}
inline bool edgeExist(int x,int y)
{
makeRoot(x);
return findRoot(y)==x;
}
inline bool link(int x,int y)
{
if(edgeExist(x,y)) return false;
softRoot(y);
link_pushup(x,y);
fa(x)=y;
pushup(y);
return true;
}
inline bool cut(int x,int y)
{
if(!edgeExist(x,y)) return false;
softRoot(y);
cut_pushup(x,y);
lc(y)=fa(x)=0;
pushup(x);
return true;
}
#undef fa
#undef lc
#undef rc
#undef sonTp
};
不得不说以前对于 LCT 的理解真是浅薄,实际上 LCT 有 \(4\) 种 \(pushup\)
一种是已知节点儿子,更新节点信息,也就是第一个 \(pushup\)
第二种是在 \(access\) 过程中如果遇到虚实边的转化,需要进行虚实儿子的转化,就是 \(access\_pushup\)
第三种是 \(link\) 时会增加儿子,和虚实转化不太一样,还要有一个 \(link\_pushup\)
第四种是 \(cut\) 时,删除儿子,没有逆元的话就和 \(link\_pushup\) 不能混用
但实际写一下你就会发现几种 \(pushup\) 实际上很接近,但确实不一样
然后就是 $$
update 2021.10.28
update 2022.6.10 重写了
fhq-Treap
template<typename _Tp,_Tp maxVal,int N=1>
class Treap
{
private:
void (*pushup)(_Tp&,_Tp&,_Tp&);
const _Tp INF;
mutable int Root,cnt;
vector<int>del_list;
class Node
{
public:
mutable int ch[2],rnd,siz;
mutable _Tp v;
Node()=default;
};
vector<Node> node;
inline void update(int x)
{
node[x].siz=node[node[x].ch[0]].siz+node[node[x].ch[1]].siz+1;
if(pushup==NULL) return;
pushup(node[x].v,node[node[x].ch[0]].v,node[node[x].ch[1]].v);
}
inline int newNode(int x)
{
static int tmp;
if(del_list.empty())
{
if(++cnt>=node.size())
node.emplace_back(Node());
tmp=cnt;
}
else tmp=del_list.back(),del_list.pop_back();
node[tmp].rnd=rand()*rand(),node[tmp].v=x,node[tmp].siz=1,node[tmp].ch[0]=node[tmp].ch[1]=0;
return tmp;
}
inline void init()
{
node.resize(N);
Root=cnt=0;
insert(-INF),insert(INF);
}
public:
inline void vsplit(int pos,_Tp v,int &x,int &y)
{
if(!pos)
{
x=y=0;
return;
}
if(node[pos].v<=v) x=pos,vsplit(node[pos].ch[1],v,node[pos].ch[1],y);
else y=pos,vsplit(node[pos].ch[0],v,x,node[pos].ch[0]);
update(pos);
}
inline void ssplit(int pos,int k,int &x,int &y)
{
if(!pos)
{
x=y=0;
return;
}
if(k>node[node[pos].ch[0]].siz)
x=pos,ssplit(node[pos].ch[1],k-node[node[pos].ch[0]].siz-1,node[pos].ch[1],y);
else y=pos,ssplit(node[pos].ch[0],k,x,node[pos].ch[0]);
update(pos);
}
inline int merge(int x,int y)
{
if(!x||!y) return x+y;
if(node[x].rnd<node[y].rnd)
{
node[x].ch[1]=merge(node[x].ch[1],y);
update(x);
return x;
}
node[y].ch[0]=merge(x,node[y].ch[0]);
update(y);
return y;
}
inline void insert(_Tp v)
{
int x,y;
vsplit(Root,v,x,y);
Root=merge(merge(x,newNode(v)),y);
}
inline void erase(_Tp& v)
{
int x,y,z;
vsplit(Root,v,x,z);
vsplit(x,v-1,x,y);
del_list.push_back(y);
y=merge(node[y].ch[0],node[y].ch[1]);
Root=merge(merge(x,y),z);
}
inline _Tp pre(_Tp& v)
{
int x,y,cur;
vsplit(Root,v-1,x,y);
cur=x;
while(node[cur].ch[1]) cur=node[cur].ch[1];
merge(x,y);
return node[cur].v;
}
inline _Tp nxt(_Tp& v)
{
int x,y,cur;
vsplit(Root,v,x,y);
cur=y;
while(node[cur].ch[0]) cur=node[cur].ch[0];
merge(x,y);
return node[cur].v;
}
inline int get_rank(_Tp& v)
{
int x,y,ans;
vsplit(Root,v-1,x,y);
ans=node[x].siz;
merge(x,y);
return ans;
}
inline _Tp kth(int k)
{
++k;
int x,y,cur;
ssplit(Root,k,x,y);
cur=x;
while(node[cur].ch[1]) cur=node[cur].ch[1];
merge(x,y);
return node[cur].v;
}
inline _Tp operator [] (int k){return kth(k);}
Treap():INF(maxVal)
{
pushup=NULL;
init();
}
Treap(void(*change)(_Tp&,_Tp&,_Tp&)):INF(maxVal)
{
pushup=change;
init();
}
Treap(_Tp* st,_Tp* ed):INF(maxVal)
{
pushup=NULL;
init();
while(st<ed) insert(*st++);
}
Treap(_Tp* st,_Tp* ed,void(*change)(_Tp&,_Tp&,_Tp&)):INF(maxVal)
{
pushup=change;
init();
while(st<ed) insert(*st++);
}
};
再次感谢人形魔芋
支持[]访问(第k大),其他函数功能和splay相同,和LCT一样,需要在构造时加入上传规则(不写则默认为NULL,什么也不做)
定义时传入的参数含义从左往右依次为节点类型,无穷值(需重载负号),预设大小
构造函数有:
1.无参数,自动插入\(\pm INF\),上传规则为空
2.一个参数,表示一个带三个_Tp类型参数的void函数指针(具体同LCT),即上传规则
3.两个参数,表示将一段连续内存作为初始值
4.三个参数,前两个参数表示一段连续内存做初始值,第三个参数表示上传规则
线段树
在这里
upd 2022.4.9
取模意义下的int
namespace ModInt
{
static int mod=998244353;
static vector<int> Inv;
class mint
{
private:
int num;
public:
inline void show() {write_(num);}
~mint()=default;
mint()=default;
mint(int x,int y):num(mint(x)/mint(y)){}
mint(int x):num(x){if(num<0) num%=mod,num+=mod;if(num>=mod) num%=mod;}
mint operator + (const mint& a){int c=a.num+num;return mint(c>=mod?c-mod:c);}
friend mint operator - (const mint& a,const mint& b){int c=a.num-b.num;return mint(c<0?c+mod:c);}
friend mint operator * (const mint& a,const mint& b){ll c=(ll)a.num*b.num;return mint(c>=mod?c%mod:c);}
friend mint operator / (const mint& a,const mint& b){if(Inv.size()<(unsigned)(b.num+2))return a*inv(b);return a*Inv[b.num];}
friend mint operator / (int a,mint b){return mint(a)/b;}
friend mint operator / (mint a,int b){return a/mint(b);}
void operator +=(const mint& x){int c=x.num+num;num=c>=mod?c-mod:c;}
void operator -=(const mint& x){int c=num-x.num;num=c<0?c+mod:c;}
int operator &(const int &x){return x#}
mint operator &(const mint &x){return mint(x.num&num);}
friend int operator &(const int& x,const mint &y){return x&(y.num);}
int operator |(const int &x){return x|num;}
mint operator |(const mint &x){return mint(x.num|num);}
friend int operator |(const int& x,const mint &y){return x|(y.num);}
int operator ^(const int &x){return x^num;}
mint operator ^(const mint &x){return mint(x.num^num);}
friend int operator ^(const int& x,const mint &y){return x^(y.num);}
mint operator >> (const int& x) {return mint(num>>=x);}
void operator >>=(const int& x) {num>>=x;}
mint operator << (int x)
{
static int tw,d;
static ll tmp;
tw=2,d=1;
while(x) (x&1)&&(tmp=(ll)d*tw,d=(tmp>=mod?tmp%mod:tmp),1),tmp=(ll)tw*tw,tw=(tmp>=mod?tmp%mod:tmp),x>>=1;
tmp=(ll)num*d,tmp=(tmp>=mod?tmp%mod:tmp);
return mint(tmp);
}
friend void operator <<=(mint x,int y){x.num=x<<y;}
friend mint operator << (mint x,mint y){return x<<(int)y;}
friend mint operator << (int x,mint y){return mint(x)<<(int)y;}
friend mint pow(mint d,mint x);
friend mint inv(mint a){return pow(a,mint(mod-2));}
mint operator - () {mint m(-num);return m;}
mint operator ++ (int) {++num;return *this;}
mint operator ++ () {mint c(num);return ++num,c;}
mint operator -- (int) {--num;return *this;}
mint operator -- () {mint c(num);return --num,*this;}
friend mint operator + (mint x,int y) {int q=x.num+y;q=q>=mod?q-mod:q;return mint(q);}
friend mint operator - (mint x,int y) {int q=x.num-y;q=q<0?q+mod:q;return mint(q);}
operator int(){return num;}
friend mint operator * (mint x,int y) {return x*mint(y);}
friend mint operator * (int x,mint y) {return mint(x)*y;}
void operator *= (const mint x){ll l=(ll)x.num*num;num=l>=mod?l%mod:l;}
void operator *= (const int x){ll l=(ll)num*x;num=(l>=mod?(l>=(mod<<1)?l%mod:l-mod):l);}
void operator += (int x){num+=x,num=num>=mod?num-mod:num;}
void operator -= (int x){num-=x;num=num<0?num+mod:num;}
void operator /= (mint b){num=num/b.num;}
bool operator!(){return !num;}
friend ostream &operator<<(ostream &out , const mint &a){out<<a.num;return out;}
};
inline void read_(mint &x)
{
int fi=1;x=0;
char op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1;
while(op>='0'&&op<='9') x=x*10+(op^48),op=getchar();
x=x*mint(fi);
return;
}
inline mint pow(mint d,mint x){mint as=1;while(x)as=(x&1?as*d:as),d=d*d,x>>=1;return as;}
inline void InvInit(unsigned int n)
{
if(Inv.size()>n) return;
if(n>Inv.max_size()) {assert("Too much number!");return;}
unsigned int tmp=Inv.size()-1;
Inv.resize(n+4,0);
if(tmp<1) Inv[1]=1;
for(unsigned int i=max(2u,tmp);i<=n;++i) Inv[i]=(ll)Inv[mod%i]*(mod-mod/i)%mod;
}
};
using ModInt::mint;
使用时修改mod的值即可,其它使用和int没有区别,支持cout,不支持cin,scanf,printf(太慢),手写了一个读入函数\(read_()\),个人比较喜欢引用版(不会有人不会改快读吧)
大概可以避免手写一串函数嵌套做多项式运算~
update2021.11.10:温馨提醒,常数巨大,大概是2倍,因为没有懒取模的优化,虽然用-优化了\(\pm\),但还是很慢,不过使用\(+=,-=,*=\)可以较为显著的缩小常数(构造函数里面有判断),不过O2神力之后和正常的就没有区别了,但用位运算变成了最慢的运算之一
update:2021.11.12 可以使用mint做下标了,增加了线筛逆元优化除法(可能会反复用)
2-SAT板子
namespace two_sat
{
#define two_sat_size 2000005
int n;
int tot,nex[two_sat_size<<2],to[two_sat_size<<2],head[two_sat_size];
inline void add(int u,int v){nex[++tot]=head[u],to[head[u]=tot]=v;}
int fa[two_sat_size],top_mark[two_sat_size],cnt,dep[two_sat_size];
inline int find_fa(int x){return x==fa[x]?x:fa[x]=find_fa(fa[x]);}
inline void dfs(int now)
{
for(int i=head[now],v=to[i];i;v=to[i=nex[i]])
{
if(!dep[v]) dep[v]=dep[now]+1,dfs(v);
if(dep[find_fa(v)]>0)
{
if(dep[find_fa(now)]<dep[fa[v]]) fa[fa[v]]=fa[now];
else fa[fa[now]]=fa[v];
}
}
dep[now]=-1,top_mark[now]=++cnt;
}
inline void solve()
{
for(int i=1;i<=n<<1;++i) fa[i]=i,dep[i]=0;//init
for(int i=1;i<=n<<1;++i)
if(!dep[i])
dfs(i);
}
inline bool possible()
{
for(int i=1;i<=n;++i) if(find_fa(i)==find_fa(i+n)) return false;
return true;
}
inline bool is_true(int x){return top_mark[x]<top_mark[x+n];}
inline void add(int x,int a,int y,int b)
{
if(x==y&&a!=b) return;
add(x+(a&1?n:0),y+(b^1?n:0)),
add(y+(b&1?n:0),x+(a^1?n:0));
}
inline void not_equal(int x,int y) {add(x,0,y,0),add(x,1,y,1);}
#undef two_sat_size
};
update 2021.11.18 自己写了2-SAT的一些题,发现很多都是板子,重复地打很麻烦,于是封装了一个板子
\(possible\,\)表示是否有解,\(is\_true\,\)返回一个变量的取值(事实上,这可能是不唯一的),\(solve\,\)将重新计算答案(\(\Theta(n+m)\))
\(not_equal\,\)选定两个变量值不同
\(add\,\)有四个参数,依次记为\(\,x,a,y,b\),表示\(\,x=a\ \vee\ y=b\,\),不能删边,想不到很好的实现方法
AC自动机
template<int N,int son_size=26>
class AC_machine
{
private:
class Node
{
public:
int end;
int end_id;
int fail;
int son[son_size];
int g_fail;
Node()=default;
}node[N];
int cnt;
static inline int turn(char ch){return ch-'a';}
int vir_alloc(){return ++cnt;}
int (*make_id)(char);
public:
AC_machine():make_id(turn){}
AC_machine(int (*change)(char)):make_id(change){}
int size(){return cnt;}
void insert(string s,int id=0)
{
int l=s.size(),now=0,tmp=0;
for(int i=0;i<l;++i)
if(!node[now].son[tmp=make_id(s[i])]) now=node[now].son[tmp]=vir_alloc();
else now=node[now].son[make_id(s[i])];
++node[now].end,node[now].end_id=id;
}
void build()
{
static queue<int> q;
node[0].fail=0;
for(int i=0;i<son_size;++i)
if(node[0].son[i])
node[node[0].son[i]].fail=0,
q.push(node[0].son[i]);
int now;
while(!q.empty())
{
now=q.front(),q.pop();
for(int i=0;i<son_size;++i)
if(node[now].son[i])
{
node[node[now].son[i]].fail=node[node[now].fail].son[i];
if(node[node[node[now].son[i]].fail].end) node[node[now].son[i]].g_fail=node[node[now].son[i]].fail;
else node[node[now].son[i]].g_fail=node[node[node[now].son[i]].fail].g_fail;
q.push(node[now].son[i]);
}
else
node[now].son[i]=node[node[now].fail].son[i];
}
}
int count(string s)//不重复
{
static bitset<N> vis;
vis.reset();
int len=s.size(),now=0,ans=0;
for(int i=0;i<len;++i)
{
now=node[now].son[make_id(s[i])];
for(int t=now;t&&!vis[t];t=node[t].g_fail)
ans+=node[t].end,
vis[t]=1;
}
return ans;
}
void clear()
{
for(int i=0;i<=cnt;++i)
memset(node[i].son,0,sizeof node[i].son),
node[i].g_fail=node[i].fail=node[i].end=0;
cnt=0;
}
};
两个模板参数,表示自动机的大小和trie的叉数(可选),构造时默认字符都是小写字母,可以自己写其他从char到int的映射写到构造函数里,好像只能统计出现次数~
SGLT
一种 \(WBLT\),但是不能旋转,有点拉
namespace SGLT
{
const double alpha=0.72;//平衡因子
bool fail;
class node
{
public:
int c[2];//儿子
int siz,cnt;//树结构上的大小,键值数
int val;//子树最大值
node()=default;
}; node s[maxn<<3]; int cnt;
vector<int> del_list;
inline int vir_alloc()
{
if(!del_list.empty()) { int tp=del_list.back(); del_list.pop_back(); return tp; }
return ++cnt;
}//申请新节点
#define ls(x) s[x].c[0]
#define rs(x) s[x].c[1]
#define leaf(x) (!s[x].c[0]&&!s[x].c[1])
inline bool bal(int x)//判断是否平衡
{ return min(s[ls(x)].siz,s[rs(x)].siz)==0||max(s[ls(x)].siz,s[rs(x)].siz)<=s[x].siz*alpha; }
inline void pushup(int x)
{
if(leaf(x)) return;
s[x].siz=s[x].cnt=0,s[x].val=-inf;
if(ls(x))
{
if(s[ls(x)].cnt==0) del_list.emplace_back(ls(x)),s[ls(x)]=node(),ls(x)=0;
else
{
chkmax(s[x].val,s[ls(x)].val);
s[x].siz+=s[ls(x)].siz,s[x].cnt+=s[ls(x)].cnt;
}
}//有时左右节点需要不同的处理,所以分开了
if(rs(x))
{
if(s[rs(x)].cnt==0) del_list.emplace_back(rs(x)),s[rs(x)]=node(),rs(x)=0;
else
{
chkmax(s[x].val,s[rs(x)].val);
s[x].siz+=s[rs(x)].siz,s[x].cnt+=s[rs(x)].cnt;
}
}
}//更新节点信息
vector<int> flat;//拍扁数组,stl重度依赖
inline void rebuild_dfs(int x)
{
if(leaf(x)) { flat.emplace_back(x); return; }
if(ls(x)) rebuild_dfs(ls(x));
if(rs(x)) rebuild_dfs(rs(x));
del_list.emplace_back(x),s[x]=node();
}//拍扁
inline int lift(int l,int r)
{
if(r<l) return 0;
if(l==r) return flat.at(l);
int mid=(l+r)>>1,p=vir_alloc();
s[p].c[0]=lift(l,mid);
s[p].c[1]=lift(mid+1,r);
pushup(p);
return p;
}//提起
inline int rebuild(int x)
{
if(!flat.empty()) flat.clear();//如果shrink了会很慢
rebuild_dfs(x);
x=lift(0,flat.size()-1);
return x;
}//重构
inline bool insert(int &x,int v)
{
if(!x) { x=vir_alloc(),s[x].siz=s[x].cnt=1,s[x].val=v; return 0; }
if(leaf(x))
{
// debug_out("leafy");
if(s[x].val==v) { ++s[x].cnt; return 0; }
int p=vir_alloc(),k=s[x].val>v;
s[x].c[k]=p;
s[p].siz=1,s[p].cnt=s[x].cnt,s[p].val=s[x].val;
s[x].c[k^1]=p=vir_alloc();
s[p].siz=1,s[p].cnt=1,s[p].val=v;
pushup(x);
return 0;
} bool t=0,k=0;
if(!ls(x)||s[ls(x)].val<v) t=insert(rs(x),v),k=1;
else t=insert(ls(x),v),k=0;
pushup(x);
if(!bal(x)) return 1;
if(t)
{
s[x].c[k]=rebuild(s[x].c[k]);
pushup(x);
} return 0;
}
inline void erase(int x,int v)
{
if(!x) return;
if(leaf(x))
{
if(s[x].val!=v) { fail=1; return; }
if(--s[x].cnt==0) s[x].siz=0,fail=0;
return;
}
if(!ls(x)||s[ls(x)].val<v) erase(rs(x),v);
else erase(ls(x),v);
pushup(x);
return;
}
inline int kth(int x,int k)
{
if(!x||s[x].cnt<k) return -inf;
if(leaf(x)) return s[x].val;
if(!ls(x)||s[ls(x)].cnt<k) return kth(rs(x),k-s[ls(x)].cnt);
return kth(ls(x),k);
}
inline int sum(int x,int v)
{
if(!x) return 0;
if(leaf(x))
{
if(s[x].val<=v) return s[x].cnt;
return 0;
}
if(!ls(x)||s[ls(x)].val<v) return s[ls(x)].cnt+sum(rs(x),v);
return sum(ls(x),v);
}
inline int rnk(int x,int v) { return sum(x,v-1)+1; }
inline int pre(int x,int v) { return kth(x,sum(x,v-1)); }
inline int nxt(int x,int v) { return kth(x,sum(x,v)+1); }
class scapeGoatTree
{
private:
int rt;
inline void shrink(int x)
{
if(ls(x)) shrink(ls(x));
if(rs(x)) shrink(rs(x));
del_list.emplace_back(x),s[x]=node();
}
public:
inline void insert(int v) { if(SGLT::insert(rt,v)) rt=SGLT::rebuild(rt); }
inline bool erase(int v) { SGLT::erase(rt,v); return fail; }//删除不需要重构,返回是否删除成功
inline int kth(int k) { return SGLT::kth(rt,k); }
inline int rnk(int v) { return SGLT::rnk(rt,v); }
inline int pre(int v) { return SGLT::pre(rt,v); }
inline int nxt(int v) { return SGLT::nxt(rt,v); }
inline void clear() { rt=0; }//只是清除数据
inline void shrink() { shrink(rt); }//清除数据和空间
scapeGoatTree()=default;
};
} SGLT::scapeGoatTree sgt;
DSU
不带按秩合并的版本,这样可以确定父亲的方向。
class DSU
{
private:
int N;
vector<int> fa;
public:
inline void resize(int n)
{
N=n;
fa.resize(n,-1);
urep(i,0,n-1) fa[i]=-1;
}
inline void init(int n) { resize(n+1); }
inline int find(int x) { return fa[x]==-1?x:fa[x]=find(fa[x]); }
inline void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return;
fa[y]=x;
}
inline bool check(int x,int y) { return find(x)==find(y); }
inline int size() { return N; }
DSU()=default;
DSU(int n) { init(n); }
};
按大小合并的版本,效率和按秩合并差不多,就不写按秩合并了。
class DSU
{
private:
int N;
vector<int> fa,siz;
public:
inline void resize(int n)
{
N=n;
fa.resize(n,-1);
siz.resize(n,1);
urep(i,0,n-1) fa[i]=-1,siz[i]=1;
}
inline void init(int n) { resize(n+1); }
inline int find(int x) { return fa[x]==-1?x:fa[x]=find(fa[x]); }
inline void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return;
if(siz[x]<siz[y]) swap(x,y);
siz[x]+=y,fa[y]=x;
}
inline bool check(int x,int y) { return find(x)==find(y); }
inline int size(int x) { return siz[find(x)]; }
inline int size() { return N; }
DSU()=default;
DSU(int n) { init(n); }
};
带花树
依赖DSU
,必须使用第一个DSU
,即不启发式合并的版本,不然会错。
class blossomy_tree
{
private:
int n;
DSU s;
queue<int> q;
int dfncnt;
veci* e;
int *match,*pre,*col,*dfn;
inline int lca(int x,int y)
{
++dfncnt;
x=s.find(x),y=s.find(y);
while(dfn[x]!=dfncnt)
{
dfn[x]=dfncnt;
x=s.find(pre[match[x]]);
if(y) swap(x,y);
} return x;
}
inline void blossom(int x,int y,int w)
{
while(s.find(x)!=w)
{
pre[x]=y;
y=match[x];
if(col[y]==2) col[y]=1,q.push(y);
if(s.find(x)==x) s.merge(w,x);
if(s.find(y)==y) s.merge(w,y);
x=pre[y];
}
}
inline bool getMatch(int x)
{
irep(i,1,n) col[i]=pre[i]=0;
s.init(n);
q=queue<int>();
while(!q.empty()) q.pop();
q.push(x);
col[x]=1;
while(!q.empty())
{
x=q.front(),q.pop();
for(int y:e[x])
{
if(s.check(x,y)||col[y]==2) continue;
if(!col[y])
{
col[y]=2;
pre[y]=x;
if(!match[y])
{
for(int k=y,p;k;k=p)
p=match[pre[k]],
match[k]=pre[k],
match[pre[k]]=k;
return 1;
}
col[match[y]]=1;
q.push(match[y]);
}
else
{
int w=lca(x,y);
blossom(x,y,w);
blossom(y,x,w);
}
}
}
return 0;
}
public:
inline int getAns(int N)
{
n=N;
dfncnt=0;
int r=0;
irep(i,1,n) if(!match[i]) r+=getMatch(i);
return r;
}
blossomy_tree()=default;
blossomy_tree(veci* e,int *match,int *pre,int *col,int *dfn):
e(e),match(match),pre(pre),col(col),dfn(dfn)
{ }
};
传入需要用到的数组,这样封装的算法类自己几乎不占用空间,而且不影响使用,同时还兼顾了开放性,使得输出过程变的容易。
代数环
template<typename numberType>
class circle_system
{
private:
using T=numberType;
using operation=T(*)(T,T);
using single_operation=T(*)(T);
public:
T zero,one;
operation add,mul;
single_operation op;
inline T sub(T a,T b) { return add(a,op(b)); }
inline void Add(T&a,T b) { a=add(a,b); }
inline void Sub(T&a,T b) { a=sub(a,b); }
inline void Mul(T&a,T b) { a=mul(a,b); }
circle_system() { }
circle_system(operation a,operation m=0,single_operation o=0):
add(a),mul(m),op(o)
{ }
circle_system(T zero,T one,operation a,operation m=0,single_operation o=0):
zero(zero),one(one),add(a),mul(m),op(o)
{ }
inline T pow(T a,int t)
{
T r=one;
while(t)
{
if(t&1) r=mul(r,a);
a=mul(a,a),t>>=1;
} return r;
}
};
字符串哈希
namespace myHash
{
struct hashNode {
u32 x,y;
hashNode()=default;
hashNode(u32 _x,u32 _y):x(_x),y(_y) { }
friend bool operator ==(hashNode a,hashNode b) { return a.x==b.x&&a.y==b.y; }
friend bool operator !=(hashNode a,hashNode b) { return a.x!=b.x||a.y!=b.y; }
friend ostream & operator << (ostream& os,hashNode& hs) {
os<<'('<<hs.x<<','<<hs.y<<')';
return os;
}
inline ull trans() { return ((ull)y<<32ull)|x; }
}; hashNode hashMiss(-1,-1);
const u32 hashModMv=time(0);
class myHashStr
{
private:
class fastmod
{
private:
ull m;
u32 l;
inline void init()
{
if(!p) cerr<<("p is zero!\n");
l=32u-__builtin_clz(p-1);
ull low=(ull(1)<<(32u+l))/p;
ull high=((1ull<<(32u+l))+(1ull<<l))/p;
while( (low>>1)<(high>>1) && l>0 ) low>>=1,high>>=1,--l;
m=high;
}
inline u32 mulhigh(u32 x,u32 y) { return ((ull)x*y)>>32ull; }
public:
u32 p;
inline u32 div(u32 x)
{
if(m>UINT32_MAX)
{
u32 t=mulhigh(x,m-(1ull<<32ull));
return (((x-t)>>1)+t)>>(l-1);
} return mulhigh(x,m)>>l;
} inline u32 mod(u32 x) { return x-p*div(x); }
fastmod():p(998244353) { init(); }
fastmod(u32 x):p(x) { init(); }
};
class safeFastmod
{
private:
fastmod p;
u32 err;
bool al2;
inline void init()
{
if(!err) cerr<<("p is zero!\n");
al2=0;
u32 r=0;
while(!(err&1u)) err>>=1u,++r;
if(err==1u)
{
err=r,al2=1;
return;
}
p=fastmod(err);
err=r;
}
public:
inline u32 div(u32 x)
{
if(al2) return x>>err;
return p.div(x>>err);
}
inline u32 mod(u32 x)
{
if(al2) return x&((1u<<err)-1u);
return x-((div(x)*p.p)<<err);
}
inline u32 mod_num() { return al2?1u<<err:p.p<<err; }
safeFastmod() { p=fastmod(998244353),err=1,al2=0; }
safeFastmod(u32 p) { err=p; init(); }
}; safeFastmod P,SQ;
u32 B1,B2,sq,maxlen;
vector<hashNode> hs;
vector<u32> pb1,pb2,pb3,pb4;//
inline u32 mul(u32 x,u32 y) { ull z=1ull*x*y; return z>=P.mod_num()?P.mod(z):z; }
inline void init_power(u32 x)
{
if(maxlen==x) return;
maxlen=x;
sq=sqrt(x)+1;
SQ=safeFastmod(sq);
pb1.resize(sq+1); pb2.resize(sq+1);
pb3.resize(sq+1); pb4.resize(sq+1);
hs.resize(x+2);
pb1[0]=1u; pb2[0]=1u;
for(u32 i=1;i<=sq;++i)
pb1[i]=mul(pb1[i-1],B1),
pb2[i]=pb2[i-1]*B2;
pb3[0]=1u; pb4[0]=1u;
pb3[1]=mul(pb1[sq-1],B1);
pb4[1]=pb2[sq-1]*B2;
for(u32 i=2;i<=sq;++i)
pb3[i]=mul(pb3[i-1],pb3[1]),
pb4[i]=pb4[i-1]*pb4[1];
}
inline u32 qpow1(u32 t)
{
if(t<=maxlen) return mul(pb1[SQ.mod(t)],pb3[SQ.div(t)]);
u32 r=1,a=B1;
while(t) {
if(t&1) r=mul(r,a);
a=mul(a,a),t>>=1;
} return r;
}
inline u32 qpow2(u32 t)
{
if(t<=maxlen) return pb2[SQ.mod(t)]*pb4[SQ.div(t)];
u32 r=1,a=B2;
while(t) {
if(t&1) r*=a;
a=a*a,t>>=1;
} return r;
}
public:
myHashStr()
{
maxlen=0,B1=114+(hashModMv>>3),B2=1919+(hashModMv>>4);
B1|=1,B2|=1;
u32 tmp=(u32)(hashModMv+998244353u);//a rand mod num
if(tmp<100000000u) tmp=1e9+7;//the mod can't be too min
P=safeFastmod(tmp);
B1=P.mod(B1);
}
myHashStr(u32 b1,u32 b2,ull p) { maxlen=0,B1=b1,B2=b2,P=safeFastmod(p); B1=P.mod(B1); }
inline void build(string& s)
{
if(s.empty()) return;
init_power(s.size());
hs[0]=hashNode(s[0],s[0]);
for(u32 i=1;i<s.size();++i)
{
hs[i].x=hs[i-1].x+mul(qpow1(i),s[i]);
if(hs[i].x>=P.mod_num()) hs[i].x-=P.mod_num();
hs[i].y=hs[i-1].y+s[i]*qpow2(i);
}
}
inline hashNode getHash(u32 l,u32 r,u32 up_to)
{
if(l>r||l>up_to||r>maxlen) return hashMiss;
if(l==0)
return hashNode(mul(hs[r].x,qpow1(up_to)),hs[r].y*qpow2(up_to));
hashNode tmp;
if(hs[r].x>=hs[l-1].x) tmp.x=hs[r].x-hs[l-1].x;
else tmp.x=P.mod_num()-hs[l-1].x+hs[r].x;
tmp.y=hs[r].y-hs[l-1].y;
tmp.x=mul(tmp.x,qpow1(up_to-l));
tmp.y*=qpow2(up_to-l);
return tmp;
} inline hashNode getHash(u32 l,u32 r) { return getHash(l,r,maxlen); }
//move it by some step so that it begins at [up_to]
inline hashNode LinkHash(hashNode a,hashNode b,u32 lenA)
{
if(a.x>=P.mod_num()) a.x=P.mod(a.x);//init
b.x=mul(b.x,qpow1(lenA));
b.y*=qpow2(lenA);
a.x+=b.x,a.y+=b.y;
if(a.x>=P.mod_num()) a.x-=P.mod_num();
return a;
}//we believe you have make a and b at the asme begining
inline void clear() { maxlen=0; }
inline void destroy()
{
hs.clear(); hs.shrink_to_fit();
pb1.clear(); pb1.shrink_to_fit();
pb2.clear(); pb2.shrink_to_fit();
pb3.clear(); pb3.shrink_to_fit();
pb4.clear(); pb4.shrink_to_fit();
}
~myHashStr() { destroy(); }
};
}
upd 2022.11.15
二维数点
template<typename coordType=int,typename valueType=int>
class static_planar_node_count
{
private:
using T=coordType;
using V=valueType;
using planar_node=k_Node::node<2,T>;
using planar_square=k_Node::node<4,T>;
planar_node* p;
planar_square* s;
V *ans;
bool haveWeight=0;
V *val;
int totx,toty;
T *x,*y;
class ex_square
{
public:
int l,r;
int column;
int id;
ex_square() { }
ex_square(int l,int r,int c,int i):l(l),r(r),column(c),id(i) { }
bool operator < (const ex_square&a) { return column<a.column; }
}; vector<ex_square> del,ins;
class insertNode
{
public:
int col,pos;
V v;
insertNode()=default;
insertNode(int c,int p,V v):col(c),pos(p),v(v) { }
bool operator < (const insertNode& b) const { return col<b.col; }
}; vector<insertNode> add;
inline void discretize(int n,int m)
{
x=new T [n+2*m+2];
y=new T [n+2*m+2];
totx=toty=0;
irep(i,1,n) x[++totx]=p[i][0],y[++toty]=p[i][1];
irep(i,1,m) x[++totx]=s[i][0],x[++totx]=s[i][2],y[++toty]=s[i][1],y[++toty]=s[i][3];
sort(x+1,x+totx+1);
sort(y+1,y+toty+1);
totx=unique(x+1,x+totx+1)-x-1;
toty=unique(y+1,y+toty+1)-y-1;
irep(i,1,n)
{
int now=lower_bound(x+1,x+totx+1,p[i][0])-x;
int pos=lower_bound(y+1,y+toty+1,p[i][1])-y;
if(haveWeight) add.emplace_back(insertNode(now,pos,val[i]));
else add.emplace_back(insertNode(now,pos,1));
}
for(int i=1;i<=m;++i)
{
int l=lower_bound(y+1,y+toty+1,s[i][1])-y,r=lower_bound(y+1,y+toty+1,s[i][3])-y;
int x1=lower_bound(x+1,x+totx+1,s[i][0])-x,x2=lower_bound(x+1,x+totx+1,s[i][2])-x;
del.emplace_back(ex_square(l,r,x1,i));
ins.emplace_back(ex_square(l,r,x2,i));
}
sort(add.begin(),add.end());
sort(del.begin(),del.end());
sort(ins.begin(),ins.end());
delete[]x;
delete[]y;
}
class BIT
{
private:
inline int lowbit(int x) { return x&(-x); }
vector<V> a;
int N;
public:
inline void init(int n)
{
a.resize(n+1);
irep(i,0,n) a[i]=0;
N=n;
}
inline void add(int x,V k) { for(;x<=N;x+=lowbit(x)) a[x]+=k; }
inline V sum(int x) { V r=0; for(;x>0;x-=lowbit(x)){r+=a[x];} return r; }
inline V sum(int l,int r) { return sum(r)-sum(l-1); }
inline void destroy() { a.clear(),a.shrink_to_fit(); }
}; BIT bit;
public:
static_planar_node_count() { }
static_planar_node_count(planar_node*point,planar_square*square,V*as,bool Weighted=0,V*v=0):
p(point),s(square),ans(as),haveWeight(Weighted),val(v)
{
totx=toty=0;
x=y=nullptr;
}
~static_planar_node_count()
{
bit.destroy();
add.clear(),del.clear(),ins.clear();
add.shrink_to_fit(),del.shrink_to_fit(),ins.shrink_to_fit();
}
inline void count(int n,int m)
{
discretize(n,m);
bit.init(toty);
unsigned delpos=0,addpos=0,inspos=0;
int l,r,id,pos;
V v;
irep(i,1,totx)
{
while(delpos<del.size()&&del[delpos].column==i)
l=del[delpos].l,r=del[delpos].r,id=del[delpos].id,
ans[id]-=bit.sum(l,r),
++delpos;
while(addpos<add.size()&&add[addpos].col==i)
pos=add[addpos].pos,v=add[addpos].v,
bit.add(pos,v),
++addpos;
while(inspos<ins.size()&&ins[inspos].column==i)
l=ins[inspos].l,r=ins[inspos].r,id=ins[inspos].id,
ans[id]+=bit.sum(l,r),
++inspos;
}
}
};