C++竞赛常用实用代码(6)

前言

所有代码中未加的默认加上“#define ll long long”

板子可改进的地方欢迎指出
多项式好难,人太懒了,现在还没搞定所有板子
上一页:C++竞赛常用实用代码(5)


计算几何

下面的几个板子之间可能互相调用

二维坐标/向量结构体

const double bt=1e-10,inf=pow(2.0,1000000000),PI=acos(-1.0);
struct pv{ //point or vector, 既表示坐标又可表示向量
	double x,y;pv(){}
	pv(double X,double Y){x=X,y=Y;}
	void READ(){scanf("%lf%lf",&x,&y);} //读入
	
	double length(){return sqrt(x*x+y*y);} //模长
	double K(){ //斜率
		if(fabs(x)<=bt)return inf;
		else return y/x;
	}
	bool operator<(const pv&b)const{ //横坐标排序(多用于求凸包)
		return x<b.x||(x==b.x&&y<b.y);
	}
	
	double operator*(const pv&b){return x*b.x+y*b.y;}//内积
	pv operator*(const double&b){return pv(x*b,y*b);}//数乘
	pv operator/(const double&b){return pv(x/b,y/b);}//数除
	pv operator+(const pv&b){return pv(x+b.x,y+b.y);}//向量加
	pv operator-(const pv&b){return pv(x-b.x,y-b.y);}//向量减
	bool operator==(const pv&b)const{ //判相等
		return fabs(x-b.x)<=bt&&fabs(y-b.y)<=bt;
	}
	bool operator!=(const pv&b)const{ //判不等
		return fabs(x-b.x)>bt||fabs(y-b.y)>bt;
	}
	pv GN(){return pv(y,-x);}//返回一个与它垂直的向量 
};
inline double cro(pv a,pv b){return a.x*b.y-b.x*a.y;} //叉积
inline double dis(pv a,pv b){return (b-a).length();} //距离

二维线段/直线结构体

struct line{ //两点确立线段/直线
	pv a,b;line(){}
	line(pv A,pv B){a=A,b=B;}
	void READ(){a.READ(),b.READ();} //读入
	pv vecm(){return b-a;} //方向向量
	void sort(){if(b<a)swap(a,b);} //规范两点位置为从左到右
	bool ni(pv c){ //判断点c是否在线段上
		if(c==a||c==b)return 1;
		return fabs(cro(c-a,c-b))<=bt&&(c-a)*(c-b)<=bt;
	}
	bool operator<(const line&y)const{ //横坐标排序
		if(a!=y.a)return a<y.a;
		else return b<y.b;
	}
};

线段、直线相关函数

inline bool check0(line A,line B){//直-直有交(不重合)
	return fabs(cro(A.vecm(),B.vecm()))>bt;
}
inline bool check1(line A,line b){//直-段有交
	return cro(A.vecm(),b.a-A.a)*cro(A.vecm(),b.b-A.a)<=bt;
}
inline bool check2(line a,line b){//段-段有交(不平行)
	if(!check0(a,b))return 0;
	return check1(a,b)&&check1(b,a);
}
inline bool check3(line a,line b){//段-段有交
	if(a.ni(b.a)||a.ni(b.b)||b.ni(a.a)||b.ni(a.b))return 1;
	return check2(a,b);
}

inline pv getcr0(line A,line B){//直-直求交(向量*比值法,交点可在线段外)
	if(!check0(A,B))return pv(inf,inf);
	double f=cro(B.a-A.a,A.b-A.a),g=cro(A.b-A.a,B.b-A.a);
	return (B.b-B.a)*(f/(f+g))+B.a;
}
inline pv getcr1(line a,line b){//段-段求交
	if(a.a==b.a||a.a==b.b)return a.a;
	if(a.b==b.a||a.b==b.b)return a.b;
	if(!check2(a,b))return pv(inf,inf);//无交或无法确定交点则返回inf
	return getcr0(a,b);
}

三角形相关函数

inline double getS(pv A,pv B,pv C){//三角形面积(叉积法)
	return fabs(cro(B-A,C-A))/2;
}
inline double getS_(double a,double b,double c){//三角形面积Ⅱ(海伦公式)
	double p=(a+b+c)/2;
	return sqrt(p*(p-a)*(p-b)*(p-c));
} 
inline pv getCT(pv A,pv B,pv C){//三角形重心
	return pv((A.x+B.x+C.x)/3,(A.y+B.y+C.y)/3);
}
inline pv getO(pv A,pv B,pv C){//三角形外心
	if(A==B)return (A+C)/2; //退化情况特判
	if(A==C||B==C)return (A+B)/2;
	pv a=(A+B)/2,b=(B+C)/2,n1=(A-B).GN(),n2=(B-C).GN();
	line l1=line(a,a+n1),l2=line(b,b+n2);
	return getcr0(l1,l2);
}

多边形相关函数

int n,m;
pv a[MAXN],b[MAXN];
//从a[n]中读入点集 
inline void getcon(){//点集转凸包 
	sort(a+1,a+1+n),m=0;
	for(int i=1;i<=n;i++){
		while(m>1&&cro(b[m]-b[m-1],a[i]-b[m-1])<=bt)m--;
		b[++m]=a[i];
	}for(int i=n-1,p=m;i>0;i--){
		while(m>p&&cro(b[m]-b[m-1],a[i]-b[m-1])<=bt)m--;
		b[++m]=a[i];
	}if(n>1)m--; //凸包(逆时针)输出至b[m] 
}
inline double polyS(){//求简单多边形面积(确保点为顺/逆时针) 
	double S=0;
	for(int i=1;i<=n;i++)S+=cro(a[i],a[i%n+1])/2;
	return fabs(S);
}
inline pv polycenter(){//求简单多边形重心(确保点为顺/逆时针) 
	pv r=pv(0,0);double sw=0,w;
	for(int i=1;i<=n;i++){
		w=cro(a[i]-pv(0,0),a[i%n+1]-pv(0,0));
		r=r+a[i]*w+a[i%n+1]*w,sw+=w;
	}
	r.x/=3*sw,r.y/=3*sw;
	return r;
}
inline bool insidep(pv x){//判断点是否在多边形内 
	for(int i=1;i<=n;i++)
		if(line(a[i],a[i%n+1]).ni(x))return 1;
	line b=line(x,pv(1e11+3,1e12+7));
	bool tot=0;
	for(int i=1;i<=n;i++)
		tot^=check2(line(a[i],a[i%n+1]),b);
	return tot;
}

伸展树Splay模板

struct splay{	//结构体
	int fa,h[2],a,siz,cnt;splay(){}
	splay(int A,int F){
		fa=F,h[0]=h[1]=0,a=A,siz=1,cnt=1;
	}
}t[MAXN];
//子树size,方便调用
#define lsum(x) (t[t[x].h[0]].siz)
#define rsum(x) (t[t[x].h[0]].siz+t[x].cnt)
int IN,root;
inline void update(int x){	//更新节点
	if(!x)return;
	t[x].siz=t[t[x].h[0]].siz+t[t[x].h[1]].siz+t[x].cnt;
}
inline bool ids(int x){return t[t[x].fa].h[1]==x;}//得到自己的儿子编号
inline void lin(int x,int y,bool f){	//连一条父子边(不更新)
	if(x)t[x].h[f]=y;if(y)t[y].fa=x;
}
inline void rott(int x){	//单旋
	if(!t[x].fa)return;
	bool d=ids(x),fd=ids(t[x].fa);
	int fa=t[x].fa,ff=t[fa].fa,sd=t[x].h[d^1];
	lin(fa,sd,d),lin(ff,x,fd),lin(x,fa,d^1);
	update(fa),update(x),update(ff);
}
inline void dospl(int x,int to=0){	//双旋到根或指定节点的儿子
	while(t[x].fa!=to){
		if(t[t[x].fa].fa!=to){
			if(ids(t[x].fa)==ids(x))rott(t[x].fa);
			else rott(x);
		}rott(x);
	}
}
inline int ins(int&x,int k){	//插入
	int fa=t[x].fa;bool d=ids(x);
	while(x&&t[x].a!=k)fa=x,d=t[x].a<k,x=t[x].h[t[x].a<k];
	if(!x)t[++IN]=splay(k,fa),t[fa].h[d]=x=IN;
	else t[x].cnt++,update(x);
	dospl(x);
	return x;
}
inline int del(int&rt,int k){	//删除
	int x=rt;
	while(x&&t[x].a!=k)x=t[x].h[t[x].a<k];
	if(!x)return rt;
	dospl(x),rt=x,t[x].cnt--,update(x);
	if(t[x].cnt)return rt;
	if(!t[x].h[1])
		return t[t[x].h[0]].fa=0,rt=t[x].h[0];
	int sf=t[x].h[1];
	while(t[sf].h[0])sf=t[sf].h[0];
	dospl(sf),rt=sf;
	lin(rt,t[x].h[0],0),update(rt);
	return rt;
}
inline int getrk(int&rt,int k){	//查找k的排名
	int res=0,x=rt;
	while(t[x].a!=k)
		res+=t[x].a<k?rsum(x):0,x=t[x].h[t[x].a<k];
	res+=lsum(x),dospl(x),rt=x;
	return res+1;
}
inline int getkr(int&rt,int k){	//查找排名为k的数
	int x=rt;
	while(lsum(x)>=k||rsum(x)<k){
		if(rsum(x)<k)k-=rsum(x),x=t[x].h[1];
		else x=t[x].h[0];
	}
	dospl(x),rt=x;
	return t[x].a;
}
inline int getpr(int&rt,int k){	//查找前缀
	int a=0,x=rt;k--;
	while(x&&t[x].a!=k){
		if(t[x].a<k)a=t[x].a,x=t[x].h[1];
		else x=t[x].h[0];
	}
	if(x)a=t[x].a;
	return a;
}
inline int getsf(int&rt,int k){	//查找后缀
	int b=0,x=rt;k++;
	while(x&&t[x].a!=k){
		if(t[x].a>k)b=t[x].a,x=t[x].h[0];
		else x=t[x].h[1];
	}
	if(x)b=t[x].a;return b;
}

分裂-合并简化 (大常数化) 版:

struct spl{
	int fa,h[2],a,siz;spl(){}
	spl(int A,int F){fa=F,h[0]=h[1]=0,a=A,siz=1;}
}t[MAXN];
int IN,root;//prepare
#define lsum(x) (t[t[x].h[0]].siz)
#define rsum(x) (t[t[x].h[1]].siz+1)
inline void update(int x){
	if(!x)return;
	t[x].siz=lsum(x)+rsum(x);
}
inline bool ids(int x){return t[t[x].fa].h[1]==x;}
inline void lin(int x,int y,bool f){if(x)t[x].h[f]=y;if(y)t[y].fa=x;}
inline void rott(int x){
	if(!t[x].fa)return;
	bool d=ids(x),fd=ids(t[x].fa);
	int fa=t[x].fa,ff=t[fa].fa,sd=t[x].h[d^1];
	lin(fa,sd,d),lin(ff,x,fd),lin(x,fa,d^1);
	update(fa),update(x),update(ff);
}
inline void dospl(int x,int to=0){
	while(t[x].fa^to){
		if(t[t[x].fa].fa^to){
			if(ids(x)==ids(t[x].fa))rott(t[x].fa);
			else rott(x);
		}rott(x);
	}
}
//split,merge
struct node{
	int x,y;node(){}
	node(int X,int Y){x=X,y=Y;}
};
inline node splii(int x,int k){//split by key
	if(!x)return node(0,0);
	while(t[x].h[t[x].a<=k])x=t[x].h[t[x].a<=k];
	dospl(x);node res;
	if(t[x].a<=k)
		res=node(x,t[x].h[1]),t[t[x].h[1]].fa=0,t[x].h[1]=0,update(x);
	else res=node(t[x].h[0],x),t[t[x].h[0]].fa=0,t[x].h[0]=0,update(x);
	return res;
}
inline node spltt(int x,int k){//split by size
	if(!x||!k)return node(0,x);
	t[x].fa=0;int fk=k;
	while(t[x].h[lsum(x)<fk]){
		if(lsum(x)<fk)fk-=lsum(x)+1,x=t[x].h[1];
		else x=t[x].h[0];
	}
	dospl(x);node res;
	if(lsum(x)<k)
		res=node(x,t[x].h[1]),t[t[x].h[1]].fa=0,t[x].h[1]=0,update(x);
	else res=node(t[x].h[0],x),t[t[x].h[0]].fa=0,t[x].h[0]=0,update(x);
	return res;
}
inline int mergg(int x,int y){
	if(!x||!y)return x^y;
	t[x].fa=t[y].fa=0;
	while(t[x].h[1])x=t[x].h[1];
	dospl(x),lin(x,y,1),update(x);
	return x;
}

手写可并堆

模板来自关于可并二叉堆的讲解

左偏树

inline bool cmp(int a,int b){return a>b;}//设置比较函数(小根堆)
inline void update(int x){	//维护左偏性
	if(t[t[x].sn[0]].dis<t[t[x].sn[1]].dis)
		swap(t[x].sn[0],t[x].sn[1]);
	t[x].dis=t[t[x].sn[1]].dis+1;
}
inline int merg(int x,int y){
	if(!x||!y)return x^y;
	if(cmp(t[x].val,t[y].val))swap(x,y);
	t[x].sn[1]=merg(t[x].sn[1],y);
	update(x);
	return x;
}

斜堆

inline bool cmp(int a,int b){return a>b;}
inline int merg(int x,int y){
	if(!x||!y)return x^y;
	if(cmp(t[x].val,t[y].val))swap(x,y);
	t[x].sn[1]=merg(t[x].sn[1],y);
	swap(t[x].sn[0],t[x].sn[1]);
	return x;
}

随机堆

inline bool cmp(ll a,ll b){return a>b;}
inline int mergh(int x,int y){
	if(!x||!y)return x^y;
	if(cmp(t[x].val,t[y].val))swap(x,y);
	bool o=rand()&1;
	if(!t[x].sn[o^1])o^=1;
	t[x].sn[o]=mergh(t[x].sn[o],y);
	return x;
}
inline bool cmp(ll a,ll b){return a>b;}
mt19937 Rand(*new(int));
inline int mergh(int x,int y){
	if(!x||!y)return x^y;
	if(cmp(t[x].val,t[y].val))swap(x,y);
	int o=Rand()&1;
	if(!t[x].sn[o^1])o^=1;
	t[x].sn[o]=mergh(t[x].sn[o],y);
	return x;
}

可持久化

随机堆的可持久化

inline bool cmp(ll a,ll b){return a>b;}
inline int mergh(int x,int y){
	if(!x||!y)return x^y;
	if(cmp(t[x].val,t[y].val))swap(x,y);
	int o=rand()&1;
	if(!t[x].sn[o^1])o^=1;
	int res=++IN;t[res]=t[x];
	t[res].sn[o]=mergh(t[x].sn[o],y);
	return res;
}

Nimber乘法及系列运算

64位无符号Nimber

#define exp e_x_p   //恶心!居然是关键字!
bool pre_ok;
const uns ll nimg=258,nod=(1<<16)-1;
uns ll ln[1<<16],exp[1<<16];

inline uns ll nimul(uns ll a,uns ll b){//Nimber-multiplication
	if(a<2||b<2)return a*b;
	if(pre_ok&&a<=nod&&b<=nod)
		return exp[(ln[a]+ln[b])%nod];
	int cp=32;
	while(cp>1&&a<(1ull<<cp)&&b<(1ull<<cp))cp>>=1;
	uns ll ah=a>>cp,a1=a^(ah<<cp),bh=b>>cp,b1=b^(bh<<cp);
	uns ll g=nimul(a1,b1);
	return nimul(nimul(ah,bh),1ull<<(cp-1))^g^((nimul(ah^a1,bh^b1)^g)<<cp);
}

inline void init(){//0~65535以内预处理
	exp[0]=1,exp[1]=nimg;
	for(int i=2;i<nod;i++)exp[i]=nimul(exp[i-1],nimg);
	for(int i=0;i<nod;i++)ln[exp[i]]=i;
	pre_ok=1;
}

inline uns ll ksm(uns ll a,uns ll b){//Nimber快速幂
	uns ll res=1;
	for(;b;b>>=1,a=nimul(a,a))if(b&1)res=nimul(res,a);
	return res;
}

inline uns ll getinv(uns ll a){//Nimber求逆元
	return ksm(a,0ull-2);
}

(转置原理)多项式多点求值

解析
全家桶都没上,直接来多点求值了。

#define ll long long
const ll MOD=998244353;
#define arr vector<ll>
inline ll ksm(ll a,ll b,ll mo){//快速幂
	ll res=1;
	for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
	return res;
}
//NTT begin
#define g 3ll
int rev[MAXN<<1];//MAXN要设得足够大
ll omg[MAXN<<1];
inline int NTT(arr&a,int inv){
	int n=a.size(),bit=1;
	while((1<<bit)<n)bit++;
	n=(1<<bit);
	if((int)a.size()<n)a.resize(n);
	for(int i=0;i<n;i++){
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		if(i<rev[i])swap(a[i],a[rev[i]]);
	}ll tmp,x,y;omg[0]=1;
	for(int m=1,mi=(MOD-1)>>1,om;m<n;m<<=1,mi>>=1){
		tmp=ksm(g,inv<0?MOD-1-mi:mi,MOD),om=0;
		for(int i=1;i<m;i++)omg[i]=omg[i-1]*tmp%MOD;
		for(int i=0;i<n;i+=(m<<1),om=0)
			for(int j=i;j<i+m;j++,om++)
				x=a[j],y=a[j+m]*omg[om]%MOD,
				a[j]=(x+y)%MOD,a[j+m]=(x-y+MOD)%MOD;
	}if(inv<0){
		ll iv=ksm(n,MOD-2,MOD);
		for(int i=0;i<n;i++)a[i]=a[i]*iv%MOD;
	}return n;
}
#undef g
//NTT end
//pre-functions begin
inline void getinv(arr&a){//多项式求逆(不依赖多项式乘法函数,减小常数)
	arr f,h;
	int n=a.size(),m=1,N=0;
	h.push_back(ksm(a[0],MOD-2,MOD));
	while(m<n){
		m<<=1,f=a,f.resize(m);
		f.resize(m<<1),h.resize(m<<1);
		N=NTT(f,1),NTT(h,1);
		for(int i=0;i<N;i++)h[i]=(MOD+2-f[i]*h[i]%MOD)*h[i]%MOD;
		NTT(h,-1),h.resize(m);
	}a=h,a.resize(n);
}
inline arr mul(arr a,arr b){//返回新数组式多项式乘法
	int n=a.size(),m=b.size();
	a.resize(n+m-1),b.resize(n+m-1);
	int N=NTT(a,1);NTT(b,1);
	for(int i=0;i<N;i++)a[i]=a[i]*b[i]%MOD;
	NTT(a,-1),a.resize(n+m-1);
	return a;
}
inline arr demul(arr a,arr b){//减法卷积
	int n=a.size(),m=b.size();
	if(n<m)return arr();
	reverse(b.begin(),b.end());
	a.resize(n+m-1),b.resize(n+m-1);
	int N=NTT(a,1);NTT(b,1);
	for(int i=0;i<N;i++)a[i]=a[i]*b[i]%MOD;
	NTT(a,-1);
	for(int i=0;i<=n-m;i++)a[i]=a[i+m-1];
	a.resize(n-m+1);
	return a;
}
//pre-functions end
//segment tree begin
arr t[MAXN<<2];
ll ans[MAXN];
inline void build(int x,int l,int r,const arr&a){//求T
	if(l==r){
		t[x].clear(),t[x].push_back(1);
		t[x].push_back((MOD-a[l])%MOD);
		return;
	}int mid=(l+r)>>1;
	build(x<<1,l,mid,a),build(x<<1|1,mid+1,r,a);
	t[x]=mul(t[x<<1],t[x<<1|1]);
}
inline void godown(int x,int l,int r,arr F){//求转置F'
	if(l==r){ans[l]=F[0];return;}
	int mid=(l+r)>>1;
	godown(x<<1,l,mid,demul(F,t[x<<1|1]));
	godown(x<<1|1,mid+1,r,demul(F,t[x<<1]));
}
//segment tree end
inline void solve(arr&b,arr&a){
	int n=max(b.size(),a.size());
	b.resize(n<<1),a.resize(n);
	build(1,0,n-1,a);
	getinv(t[1]);
	godown(1,0,n-1,demul(b,t[1]));
}

奇素数二次剩余

到(6)了才写二次剩余,我果然还是太菜了

inline ll ksm(ll a,ll b,ll mo){//快速幂
	ll res=1;
	for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
	return res;
}
inline ll sqrtmod(ll a,ll MOD){
	static mt19937_64 E(*new(int));
	ll ler=ksm(a,(MOD-1)>>1,MOD);
	if(!ler)return 0;
	if(ler^1)return -1;
	ll b=E()%MOD;
	while(ksm(b,(MOD-1)>>1,MOD)==1)b=E()%MOD;
	int t=0;ll s=MOD-1;
	while(~s&1)s>>=1,t++;
	ll x=ksm(a,(s+1)>>1,MOD),iva=ksm(a,MOD-2,MOD);
	for(int i=t-1;i>0;--i){
		ll w=ksm(iva*x%MOD*x%MOD,1ll<<(i-1),MOD);
		if(w==1)continue;
		x=x*ksm(b,s<<(t-i-1),MOD)%MOD;
	}
	return min(x,MOD-x);
}

Min_25筛模板

ll n,pr[MAXN/10];
bool nop[MAXN];
int le;
inline void ad(ll&a,ll b){a+=b;if(a>=MOD)a-=MOD;}
int id[MAXN],di[MAXN],m;
ll B,w[MAXN],pre[MAXN];
ll g[MAXN];
inline int ID(ll x){return x<=B?id[x]:di[n/x];}

inline ll F(ll p,ll e){//求f(p^e)
	...
}
inline ll sumF(ll x){//求完全积性假函数的前缀和(去掉1)
	...
}
inline void prepare_Min25(){
	B=sqrt(n);
	nop[0]=nop[1]=1;
	for(int a=2;a<=B;a++){//线性筛
		if(!nop[a])pr[++le]=a;
		for(int i=1,u;i<=le;i++){
			u=pr[i]*a;
			if(u>B)break;
			nop[u]=1;
			if(a%pr[i]==0)break;
		}
	}
	m=0,pre[0]=0;
	for(int i=1;i<=le;i++)pre[i]=(pre[i-1]+F(pr[i],1))%MOD;
	for(ll i=1,la,v;i<=n;i=la+1){//预处理映射、赋初值
		v=n/i,la=n/v;
		if(v<=B)id[v]=++m;
		else di[n/v]=++m;
		w[m]=v,g[m]=sumF(v);
	}
	for(int i=1;i<=le;i++){//亚线性递推求g
		ll z=pr[i]*pr[i];
		for(int j=1;j<=m;j++){
			if(z>w[j])break;
			ad(g[j],MOD - (g[ID(w[j]/pr[i])] - pre[i-1]+MOD) * F(pr[i],1) %MOD);
		}
	}//如果有多个g用同样的方法求
}
inline ll askf(ll x,int i){//递归求f
	if(x<2||pr[i]>x)return 0;
	ll res=(g[ID(x)]-pre[i-1]+MOD)%MOD;
	for(int j=i;j<=le;j++){
		if(pr[j]*pr[j]>x)break;
		for(ll e=pr[j],k=1;e*pr[j]<=x;e*=pr[j],k++)
			ad(res,F(pr[j],k)*askf(x/e,j+1)%MOD),ad(res,F(pr[j],k+1));
	}return res;
}
ll f[MAXN];
void getf(){//递推求f
	for(int i=1;i<=m;i++)f[i]=g[i];
	for(int i=le;i>0;i--){
		ll z=pr[i]*pr[i];
		for(int j=1;j<=m;j++){
			if(z>w[j])break;
			ll h=pr[i],to;
			for(int e=1;(to=h*pr[i])<=w[j];e++,h=to)
				f[j]+=(f[ID(w[j]/h)]-pre[i])*F(pr[i],e)+F(pr[i],e+1);
		}
	}
}

久等了!动态树LCT模板

参考这道题的实现吧

struct spl{
	int fa,h[2],a,s;bool lz;spl(){}
	spl(int A){fa=h[0]=h[1]=0,a=s=A,lz=0;}
}t[MAXN];
inline void cover(int x){
	if(!x)return;
	t[x].lz^=1,swap(t[x].h[0],t[x].h[1]);
}
inline void pushd(int x){
	if(t[x].lz)cover(t[x].h[0]),cover(t[x].h[1]),t[x].lz=0;
}
inline void update(int x){
	t[x].s=t[x].a^t[t[x].h[0]].s^t[t[x].h[1]].s;
}
inline bool sd(int x){return x==t[t[x].fa].h[1];}
inline bool isroot(int x){return x!=t[t[x].fa].h[sd(x)];}
inline void lin(int x,int y,bool f){if(x)t[x].h[f]=y;if(y)t[y].fa=x;}
inline void rott(int x){
	if(!t[x].fa||isroot(x))return;
	bool f1=sd(x),f2=sd(t[x].fa);
	int fa=t[x].fa,ff=t[fa].fa,sn=t[x].h[f1^1];
	if(isroot(fa))t[x].fa=ff;else lin(ff,x,f2);
	lin(fa,sn,f1),lin(x,fa,f1^1);
	update(fa),update(x),update(ff);
}
inline void pushtag(int x){
	if(!isroot(x))pushtag(t[x].fa);
	pushd(x);
}
inline void splay(int x){
	pushtag(x);
	while(!isroot(x)){
		if(!isroot(t[x].fa)){
			if(sd(x)==sd(t[x].fa))rott(t[x].fa);
			else rott(x);
		}rott(x);
	}
}
inline void access(int x){
	for(int y=0;x;y=x,x=t[x].fa)splay(x),t[x].h[1]=y,update(x);
}
inline void makeroot(int x){
	access(x),splay(x),cover(x);
}
inline void LINK(int x,int y){
	if(x==y)return;
	makeroot(x),access(y),splay(y);
	if(t[x].fa==0)lin(y,x,1),update(y);
}
inline void CUT(int x,int y){
	makeroot(x),access(x),splay(y);
	if(!t[y].h[0]&&t[y].fa==x)t[y].fa=0;
}
inline int query(int x,int y){
	makeroot(x),access(y),splay(x);
	return t[x].s;
}

Lyndon 分解

void Lyndon(char*s,int n){
	for(int i=1,j,k;i<=n;){
		j=i;
		for(k=i+1;k<=n&&s[j]<=s[k];k++)j=s[j]<s[k]?i:j+1;
		for(;i<=j;i+=k-j)printf("[%d,%d]\n",i,i+k-j-1);		//输出Lyndon串所在区间
	}
}

Miller-Rabin素数测试

#define ll long long
#define lll __int128
const lll E=1;
ll ksml(ll a,ll b,const ll&mo){//快速幂
	ll res=1;
	for(;b;b>>=1,a=E*a*a%mo)if(b&1)res=E*res*a%mo;
	return res;
}
mt19937_64 rng(1145141);
bool MillerRabin(const ll&p){
	if(p==2||p==3||p==5||p==7||p==11)return 1;
	if((~p&1)||!(p%3)||!(p%5)||!(p%7)||!(p%11))return 0;
	ll k=p-1,m=0;
	while(~k&1)k>>=1,m++;
	for(int cnt=35;cnt--;){
		ll a=ksml(rng()%(p-3)+2,k,p);
		if(a==1||a==p-1)continue;
		for(int t=0,tg=0;t<m;t++){
			a=E*a*a%p;
			if(!tg&&a==1)return 0;
			if(a==p-1)tg=1;
		}if(a!=1)return 0;
	}return 1;
}

Pollard-Rho质因数分解

相关函数定义见上面Miller-Rabin

ll gcd(ll a,ll b){for(;b;a%=b,swap(a,b));return a;}
ll PollardRho(const ll&n){
	while(1){
		ll d=1,x=rng()%(n-1)+1,y=x,c=rng()%(n-1)+1;
		for(int len=1,lim=127;;len<<=1,x=y,lim=127){
			bool hc=0;
			for(int cnt=len;cnt--;){
				y=(E*y*y+c)%n,d=E*d*(x-y+n)%n,lim--;
				if(x==y||!d){hc=1;break;}
				if(!lim){
					d=gcd(d,n),lim=127;
					if(d>1)return d;
				}
			}
			if(hc)break;
			d=gcd(d,n);
			if(d>1)return d;
		}
	}return 1919810;
}
unordered_map<ll,int>mp;//存质因子及其指数
void findz(ll n,int cnt){
	if(n==1)return;
	if(MillerRabin(n)){mp[n]+=cnt;return;}
	ll d=PollardRho(n);int cg=0;
	while(n%d==0)n/=d,cg++;
	findz(d,cg*cnt),findz(n,cnt);
}

Always to be continued(持续更新中)…

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值