模板整理

因为作者又懒又菜还健忘,所以要整理一下模板。

SA

char c[N];
int n,m,height[N],sa[N],rk[N],a[N],b[N],d[N];
void doubling(){
	rep(i,1,n) b[a[i]=c[i]]++; //b数组是桶,a[i]是第i个元素的第一关键字
	rep(i,2,m) b[i]+=b[i-1]; //做b的前缀和,得出每个关键字最多的名次
	per(i,n,1) sa[b[a[i]]--]=i; 
	for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){
		rep(i,n-k+1,n) d[++p]=i;
        //d[i]表示第二关键字排名为i的数,第一关键字的位置
		//第n-k+1到第n位是没有第二关键字的 排名最前
        rep(i,1,n) if(sa[i]>k) d[++p]=sa[i]-k;
        //排名为i的数 在数组中是否在第k位以后
		//如果满足(sa[i]>k) 可以作为第二关键字,将其第一关键字的位置添加进y
		//所以i枚举的是第二关键字的排名,第二关键字靠前的先入队
		rep(i,1,m) b[i]=0; //初始化桶
		rep(i,1,n) b[a[i]]++; //通过上轮结果得到第一关键字
		rep(i,2,m) b[i]+=b[i-1]; //第一关键字排名为1~i的数有多少个
		per(i,n,1) sa[b[a[d[i]]]--]=d[i],d[i]=0; //基数排序
		swap(a,d); a[sa[1]]=1; p=1;  //复制结果
		rep(i,2,n) a[sa[i]]=(d[sa[i]]==d[sa[i-1]]&&d[sa[i]+k]==d[sa[i-1]+k])?p:++p;
		//sa[i]完成排序,按排名枚举,生成下一次的第一关键字
		if(p==n) break;
    }
}
void Getheight(){
	int k=0;
	rep(i,1,n) rk[sa[i]]=i;
	rep(i,1,n){
		if(rk[i]==1) continue;
		if(k) k--; //根据定理直接计算即可
		int j=sa[rk[i]-1];
		while(j+k<=n&&i+k<=n&&c[i+k]==c[j+k]) ++k;
		height[rk[i]]=k;
	}
}

SAM

在线

struct node{
	int ch[26],len,fa,size;
}sam[N*4];
int las=1,tot=1,n;
long long ans;
void insertchar(int c){
	if(sam[las].ch[c]&&sam[las].len+1==sam[sam[las].ch[c]].len) //1
	return (void)(las=sam[las].ch[c]);
	int p=las,np=las=++tot; sam[np].size=1;
	sam[np].len=sam[p].len+1;
	for(;p&&!sam[p].ch[c];p=sam[p].fa) sam[p].ch[c]=np;
	if(!p) sam[np].fa=1; else{
		int q=sam[p].ch[c];
		if(sam[q].len==sam[p].len+1) sam[np].fa=q;
		else{
			int nq=++tot;
			if(sam[p].len+1==sam[np].len) las=nq; //2
			sam[nq]=sam[q],sam[nq].len=sam[p].len+1;
			sam[q].fa=sam[np].fa=nq; sam[nq].size=0;
			for(;p&&sam[p].ch[c]==q;p=sam[p].fa) sam[p].ch[c]=nq;
		}
	}
}

广义后缀自动机在线,添加新串时las置为1。普通后缀自动机可以使用,也可以去掉两个特判。

没啥好说的,背。

离线

queue<int>que; int pos[N]={0,1};
void buildsam(){
	que.push(1);
	while(!que.empty()){
		int p=que.front(); que.pop();
		rep(i,0,25) if(trie[p].ch[i]){
			las=pos[p];
			que.push(trie[p].ch[i]);
			insertchar(i);
			pos[trie[p].ch[i]]=las;
		}
	}
}//记录一下las随便搞

讲真代码挺好背的。

insertchar部分同普通后缀自动机。

Manacher

void manacher(){
	rep(i,1,n){
		S[i<<1]=s[i];
		S[i<<1|1]='#';
	}
	s[n<<1|1]='@';
	int r=0,c=0;
	rep(i,1,2*n){
		t[i]=r>i?min(t[2*c-i],r-i):0;
		for(;S[t[i]+i+1]==S[i-t[i]-1];t[i]++)tot++;
		if(t[i]+i>r) r=t[i]+i,c=i;
	}
}

AC自动机

void build(){
	trie[root].fail=root;
	for(ITER it=trie[root].ch.begin();it!=trie[root].ch.end();it++){
		int i=it->first;
		trie[trie[root].ch[i]].fail=root;
		que.push(trie[root].ch[i]);
	}
	while(!que.empty()){
		int p=que.front(); que.pop();
		for(ITER it=trie[p].ch.begin();it!=trie[p].ch.end();it++){
			int P=trie[p].fail,i=it->first,v=it->second;
			while(P&&P!=root&&!trie[P].ch[i]) P=trie[P].fail;
			if(trie[P].ch[i]) trie[v].fail=trie[P].ch[i];
			else trie[v].fail=root;
			que.push(v);
		}
	}
}

这种是字符集很大的情况建出来的trie图,下为正常情况:

void build(){
	trie[root].fail=root;
	rep(i,0,25){
		if(!trie[root].ch[i]){ trie[root].ch[i]=root; continue; }
		trie[trie[root].ch[i]].fail=root;
		que.push(trie[root].ch[i]);
	}
	while(!que.empty()){
		int p=que.front(); que.pop();
		rep(i,0,25){
			if(!trie[p].ch[i]) trie[p].ch[i]=trie[trie[p].fail].ch[i];
			else{
				trie[trie[p].ch[i]].fail=trie[trie[p].fail].ch[i];
				que.push(trie[p].ch[i]);
			}
		}
	}
}

平衡树合并

int newnode(int x){
	treap[++cnt]=(node){{0,0},x,1ll*rand()*rand()*rand()*rand()%19260817,1};
	return cnt;
}
void upd(int p){
	treap[p].size=treap[son(p,0)].size+treap[son(p,1)].size+1;
}
int merge(int &p,int x,int y){
	if(x==0||y==0) return p=x+y;
	if(treap[x].key<treap[y].key) merge(son(p,1),son(p=x,1),y);
	else merge(son(p,0),x,son(p=y,0));
	return upd(p),p;
}
int split(int p,int k,int &x,int &y){
	if(!p) return x=y=0;
	if(treap[son(p,0)].size>=k) split(son(p,0),k,x,son(y=p,0));
	else split(son(p,1),k-treap[son(p,0)].size-1,son(x=p,1),y);
	return upd(p),0;
}
int Split(int p,int k,int &x,int &y){
	if(!p) return x=y=0;
	if(treap[p].val<k) Split(son(p,0),k,x,son(y=p,0));
	else Split(son(p,1),k,son(x=p,1),y);
	return upd(p),0;
}
int join(int &p,int x,int y){
	if(!x||!y) return p=x+y;
	if(treap[x].key>treap[y].key) swap(x,y);
	int r1,r2; Split(y,treap[x].val,r1,r2);
	join(son(x,0),son(x,0),r1); join(son(x,1),son(x,1),r2); p=x;
	return upd(p),p;
}

FFT

const ld Pi=acos(-1.0);
struct Complex{
	ld x,y;
	Complex operator +(Complex a){ return (Complex){a.x+x,a.y+y}; }
	Complex operator -(Complex a){ return (Complex){x-a.x,y-a.y}; }
	Complex operator *(Complex a){ return (Complex){x*a.x-y*a.y,x*a.y+y*a.x}; }
};
int n,m,rev[N],lim,len;
Complex a[N],b[N];
void FFT(Complex *f,ld type){
	rep(i,0,lim-1) if(i<rev[i]) swap(f[i],f[rev[i]]);
	for(int i=1;i<lim;i<<=1){
		Complex wn=(Complex){cos(Pi/i),type*sin(Pi/i)};
		for(int j=0;j<lim;j+=(i<<1)){
			Complex w=(Complex){1,0};
			for(int k=0;k<i;k++,w=w*wn){
				Complex X=f[j+k],Y=w*f[i+j+k];
				f[j+k]=X+Y; f[i+j+k]=X-Y;
			}
		}
	}
}

NTT

void NTT(int *f,int lim,int type){
	rep(i,1,lim) if(i<rev[i]) swap(f[i],f[rev[i]]);
	for(int i=1;i<lim;i<<=1){
		int wn=Pow(G,type*(Mod-1)/(i<<1));
		for(int j=0;j<lim;j+=(i<<1)){
			for(int k=0,w=1;k<i;k++,w=1LL*w*wn%Mod){
				int x=f[j+k]%Mod,y=1LL*w*f[i+j+k]%Mod;
				f[j+k]=(x+y)%Mod;
				f[i+j+k]=(x-y+Mod)%Mod;
			}
		}
	}
	if(type==-1){
		int x=Pow(lim,Mod-2);
		rep(i,0,lim) f[i]=1LL*f[i]*x%Mod;
	}
}

LCT 几个核心科技

struct node{
	int child[2],val,sum,fa,tag;
}tree[N];
int n,m;
//Splay part
inline void upd(int p){
	tree[p].sum=tree[son(p,0)].sum^tree[son(p,1)].sum^tree[p].val;
}
inline void pushdown(int p){
	if(tree[p].tag) swap(son(p,1),son(p,0));
	tree[son(p,1)].tag^=tree[p].tag;
	tree[son(p,0)].tag^=tree[p].tag;
	tree[p].tag=0;
}
inline int soncheck(int p){
	return son(tree[p].fa,1)==p;
}
inline bool rootcheck(int p){
	return son(tree[p].fa,1)!=p&&son(tree[p].fa,0)!=p; 
}
inline void rotate(int p){
	int f=tree[p].fa,g=tree[f].fa,k=soncheck(p);
	if(!rootcheck(f)) son(g,soncheck(f))=p;
	tree[p].fa=g,son(f,k)=son(p,k^1),tree[son(p,k^1)].fa=f;
	son(p,k^1)=f,tree[f].fa=p;
	upd(f),upd(p);
}
inline void Pushtag(int p){
	if(!rootcheck(p)) Pushtag(tree[p].fa);
	pushdown(p);
}
inline void splay(int p){
	Pushtag(p);
	while(!rootcheck(p)){
		int f=tree[p].fa;
		if(!rootcheck(f)) rotate(soncheck(p)==soncheck(f)?f:p);
		rotate(p);
	}
}
//Link-Cut-tree part
void access(int p){
	for(int x=0;p;p=tree[x=p].fa) splay(p),son(p,1)=x,upd(p);
} 
void makeroot(int p){
	access(p),splay(p),tree[p].tag^=1;
}
int findroot(int p){
	access(p),splay(p);
	for(pushdown(p);son(p,0);pushdown(p=son(p,0)));
	return p;
}
void split(int x,int y){
	makeroot(x),access(y),splay(y);
}
void link(int x,int y){
	makeroot(x);
	if(findroot(y)!=x) tree[x].fa=y;
	upd(y);
}
void cut(int x,int y){
	makeroot(x);
	if(findroot(y)==x&&tree[x].fa==y&&!son(x,1)) tree[x].fa=son(y,0)=0,upd(y);
}

rotate自己推着写。LCT很多神奇操作还挺精密的,值得复习。

拉格朗日插值

int lagrange(){
	int ans=0;
	rep(i,1,n){
		int K=1;
		rep(j,1,n){
			if(i==j) continue;
			K=1LL*K*(x[i]+Mod-x[j])%Mod;
		}
		K=Pow(K,Mod-2);
		rep(j,1,n){
			if(i==j) continue;
			K=1LL*K*(k+Mod-x[j])%Mod;
		}
		ans=(ans+1LL*K*y[i]%Mod+Mod)%Mod;
	}
	return ans;
}

这个主要应该记忆一下式子。多项式+插值的思想很常用,值得掌握。

\(f(x)=\sum^{n}_{i=0} y_i\prod_{j\not=i} \frac{x-x[j]}{x[i]-x[j]}\)

另外还存在一些特殊条件下的插值,待更新。

回文自动机

struct node{
	int ch[26],fail,len,num;
}trie[N]={(node){{0},1,0,0},(node){{0},0,-1,0}};
int n,length,last,cnt=1,s[N]={26};
char c[N];
int getfail(int p,int x){
	while(s[x-trie[p].len-1]!=s[x]) p=trie[p].fail;
	return p;
}
void insert(int x){
	int p=getfail(last,x);
	if(!trie[p].ch[s[x]]){
		trie[++cnt].len=trie[p].len+2;
		int tmp=getfail(trie[p].fail,x);
		trie[cnt].fail=trie[tmp].ch[s[x]];
		trie[cnt].num=trie[trie[cnt].fail].num+1;
		trie[p].ch[s[x]]=cnt;
	}
	last=trie[p].ch[s[n]];
}

题挺少的。记得复习QwQ。

网络流

struct edge{
	int v,Next,val;
}e[N<<2];
int head[N],iter[N],cnt=1,s,t,d[N],n,m;
void addedge(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].val=w;
	e[cnt].Next=head[u];
	head[u]=cnt;
}
bool bfs(int s,int t){
	memset(d,0,sizeof(d));
	queue<int>q; q.push(s); d[s]=1;
	while(!q.empty()){
		int p=q.front(); q.pop();
		for(int ne=head[p];ne;ne=e[ne].Next){
			if(d[e[ne].v]||e[ne].val==0) continue;
			q.push(e[ne].v); d[e[ne].v]=d[p]+1;
			if(e[ne].v==t) return 1;
		}
	}
	return 0;
}//残余网络分层,既求最少到达边数。
int dinic(int p,int flow){
	if(p==t) return flow;
	int rest=flow;
	for(int &ne=iter[p];ne&&rest;ne=e[ne].Next){
		if(d[e[ne].v]!=d[p]+1||e[ne].val==0) continue;
		int k=dinic(e[ne].v,min(rest,e[ne].val));
		if(!k) d[e[ne].v]=0;
		else{
			e[ne].val-=k;
			e[ne^1].val+=k;
			rest-=k;
		}
	}
	return flow-rest;
}//建议背诵
void solve(int s,int t){
    while(bfs(s,t)){
		rep(i,1,n) iter[i]=head[i];
		while(now=dinic(s,Inf)) ans+=now;
	}
}

部分理解部分背诵,值得注意的是要记得EK费用流的原理,以备模拟费用流类题目。

费用流

queue <int> que;
bool SPFA(int s,int t){
	memset(dis,127,sizeof(dis));
	memset(flow,127,sizeof(flow));
	memset(vis,0,sizeof(vis));
	que.push(s);vis[s]=1;pre[t]=-1,dis[s]=0;
	while(!que.empty()){
		int p=que.front();que.pop();
		for(int ne=head[p];ne;ne=e[ne].Next){
			if(e[ne].flow>0&&dis[e[ne].v]>dis[p]+e[ne].cost){
				dis[e[ne].v]=dis[p]+e[ne].cost;
				pre[e[ne].v]=p;las[e[ne].v]=ne;
				flow[e[ne].v]=min(flow[p],e[ne].flow);
				if(!vis[e[ne].v]){
					que.push(e[ne].v);
					vis[e[ne].v]=true;
				}
			}
		}
		vis[p]=false;
	}
	return pre[t]!=-1;
}
void MCMF(){
	while(SPFA(s,t)){
		int p=t;
		maxflow+=flow[t];
		mincost+=flow[t]*dis[t];
		while(p!=s){
			e[las[p]].flow-=flow[t];
			e[las[p]^1].flow+=flow[t];
			p=pre[p];
		}
	}
}

BSGS

int BSGS(int x,int y){
	mp.clear();
	int sz=sqrt(Mod);
	int now=1;
	rep(i,0,sz-1){
		if(mp.find(now)==mp.end()) mp.insert(make_pair(now,i));
		now=1ll*now*x%Mod;
	}
	int inv=Pow(now,Mod-2);
	now=y;
	rep(i,0,sz){
		if(mp.find(now)!=mp.end()) return i*sz+mp[now];
		now=1ll*now*inv%Mod;
	}
	return -1;
}

原理其实挺憨憨的,不过有些小细节还是值得注意一下的。

高斯消元

int gauss(int n,int mat[N][N]){
    long long ans=1; n--;
    rep(i,1,n){
        rep(j,i+1,n){
            int x=mat[i][i],y=mat[j][i];
            while(y){
                int tmp=x/y;x%=y;swap(x,y);
                rep(k,i,n) mat[i][k]=(mat[i][k]-1LL*tmp*mat[j][k]%Mod+Mod)%Mod;
                swap(mat[i],mat[j]); ans=-ans;
            }
        }
    }
    rep(i,1,n) ans=ans*mat[i][i]%Mod;
    return (ans%Mod+Mod)%Mod;
}

上为不取模,取模更好写。

CRT

普通CRT代码很好写,写下公式。

\(M=\prod m_i\) \(M_i=\frac{M}{m_i}\) \(M_it_i=1 (mod\ \ m_i)\) \(x=\sum \frac{M}{m_i}a_it_i\)

Ex_CRT

bool ExCrt(ll k,ll a,ll p,ll &C,ll &P){
	k%=p,a%=p;
	if(!k&&a) return 0;
	if(!k&&!a) return 1;
	ll x,y,d; d=exgcd(k,p,x,y);
	if(a%d) return 0;
	p/=d,a/=d,k/=d;
	a=mul(a,(x%p+p)%p,p);
	d=exgcd(P,p,x=0,y=0);
	if((a-C)%d) return 0;
	P=P/d*p;
	C=(C+mul(mul(P/p,((a-C)%P+P)%P,P),(x%P+P)%P,P))%P;
	return 1;
}

代码段来自屠龙勇士,不过其实没啥,几步推导见下。

\(ax=b\ (mod\ p), <=> x=x_0(mod\ \frac{p}{(a,p)})\) 其中 \(ax_0+py_0=b\) 推理过程直接拓欧。

\(x=a_1\ (mod\ p_1)\&\ x=a_2\ (mod\ p_2) =>\)

\(x=x_0\ (mod\ [p_1,p_2])\) \(x\)的具体值太难写了,总之一次代式拓欧能轻松求。

点分治

ezoj 610

namespace Centroid{
	int fa[N][30],dis[N][30],dept[N];
	int rt,sz[N],son[N],SIZE,vis[N];
	vector<int>f[N],g[N];
	void Dfs(int p,int fat){
		sz[p]=1,son[p]=0;
		for(auto v:G[p]){
			if(v==fat||vis[v]) continue;
			Dfs(v,p); sz[p]+=sz[v];
			son[p]=max(son[p],sz[v]);
		}
		son[p]=max(son[p],SIZE-sz[p]);
		if(rt==0||son[p]<son[rt]) rt=p;
	}
	void DFS(int p,int fat,int rt,int dep){
		fa[p][++dept[p]]=rt,dis[p][dept[p]]=dep;
		for(auto v:G[p]){
			if(v==fat||vis[v]) continue;
			DFS(v,p,rt,dep+1);
		}
	}
	void dfs(int p){
		vis[p]=1; f[p].resize(SIZE+5),g[p].resize(SIZE+5);
		int Sz=SIZE; fa[p][++dept[p]]=p;
		for(auto v:G[p]) if(!vis[v]) DFS(v,v,p,1);
		for(auto v:G[p]){
			if(vis[v]) continue;
			rt=0,SIZE=sz[v]>sz[p]?Sz-sz[p]:sz[v];
			Dfs(v,p),dfs(rt);
		}
	}
	void solve(){
		rt=0,SIZE=n;
		Dfs(1,0); dfs(rt);
		rep(i,1,n) per(j,dept[i],1) 
			f[fa[i][j]][dis[i][j]]++,j<dept[i]?g[fa[i][j+1]][dis[i][j]]++:0;
		rep(i,1,n) rep(j,1,(int)f[i].size()-1)
			f[i][j]+=f[i][j-1],g[i][j]+=g[i][j-1];
	}
	int query(int p,int k){
		int res=0;
		per(i,dept[p],1) if(k>=dis[p][i]){
			int u=fa[p][i],v=fa[p][i+1];
			if(!f[u].empty()) res+=f[u][min((int)f[u].size()-1,k-dis[p][i])];
			if(!g[v].empty()) res-=g[v][min((int)g[v].size()-1,k-dis[p][i])];
		}
		return res;
	}
}

Miller-Robin & Pollar-Rho *

ll mul(ll x,ll y,ll Mod){
	x%=Mod;y%=Mod;
	return (x*y-(ll)(((long double)x*y+0.5)/(long double)Mod)*Mod+Mod)%Mod;
}
ll Pow2(ll x,ll y,ll Mod){
	ll ans=1;x%=Mod;
	for(;y;ans=(y&1?mul(x,ans,Mod):ans),y>>=1,x=mul(x,x,Mod));
	return ans;
}
int Pow(int x,ll y,int Mod){
	int ans=1;x%=Mod;
	for(;y;ans=(y&1?1LL*x*ans%Mod:ans),y>>=1,x=1LL*x*x%Mod);
	return ans;
}
ll f[]={2,3,5,7,11,13,17,19,23,29};
bool Miller_Robin(ll p){
	ll P=p-1,z=0,w,x;
	while(~P&1) P>>=1,z++;
	rep(i,0,10){
		w=Pow2(f[i],P,p);
		if(p<=f[i]) break;
		if(Pow2(f[i],p-1,p)!=1) return false;
		for(int _=z;_--;w=x){
			x=mul(w,w,p);
			if(x==1&&w!=1&&w!=p-1) return false;
		}
	}
	return true;
}//素数测试,很憨,背背流程。
ll Pollard_Rho(ll p,ll c){
	ll i=0,k=2,x,y;x=y=1+rand()%(p-1);
	while(1){
		x=(mul(x,x,p)+c)%p;
		ll d=__gcd((y-x+p)%p,p);
		if(d!=1&&d!=p) return d;
		if(x==y) return p;
		if(++i==k) y=x,k<<=1;
	}
}//重复一个找环的过程。
void fact(ll x){
	if(x==1) return ;
	if(Miller_Robin(x)) return (void)(tmp[++len]=x);
	ll p=x;
	for(int c=233;p==x;c--) p=Pollard_Rho(p,c);
	fact(p),fact(x/p);
}

虚树

sort(a+1,a+k+1,cmp);
st[top=1]=1;
rep(i,a[1]==1?2:1,k){
	if(!top){
		st[top=1]=a[i];
		continue;
	}
	int lca=LCA(st[top],a[i]);
	while(top>1&&dep[lca]<dep[st[top-1]]) addedge(st[top-1],st[top]),top--;
	if(dep[lca]<dep[st[top]]) addedge(lca,st[top--]);
	if((!top)||(st[top]!=lca)) st[++top]=lca;
	st[++top]=a[i];
}
if(top) while(--top) addedge(st[top],st[top+1]);ll mul(ll x,ll y,ll Mod){
	x%=Mod;y%=Mod;
	return (x*y-(ll)(((long double)x*y+0.5)/(long double)Mod)*Mod+Mod)%Mod;
}

多项式全家桶*

const int Mod=998244353;
struct Poly:vector<int>{
	Poly(int n=0){ resize(n); }
	Poly(int n,int x){ resize(n); rep(i,0,n-1) (*this)[i]=x; }
};
Poly resize(Poly f,int n){ f.resize(n); return f; }
int Pow(int x,int y){
	int res=1;
	for(y=(y%(Mod-1)+Mod-1)%(Mod-1);y;y&1?res=1ll*res*x%Mod:0,y>>=1,x=1ll*x*x%Mod);
	return res;
}
namespace Fourier{
	const int G=3,N=(1<<22)+5;
	int rev[N];
	int Getrev(int n){
		int lim=1,len=0;
		for(lim=1,len=0;lim<=n;lim<<=1,len++);
		rep(i,0,lim-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
		return lim;
	}
	void NTT(int *f,int lim,int type){
		rep(i,1,lim-1) if(i<rev[i]) swap(f[i],f[rev[i]]);
		for(int i=1;i<lim;i<<=1){
			int wn=Pow(G,type*(Mod-1)/(i<<1));
			for(int j=0;j<lim;j+=(i<<1)){
				for(int k=0,w=1;k<i;k++,w=1LL*w*wn%Mod){
					int x=f[j+k]%Mod,y=1LL*w*f[i+j+k]%Mod;
					f[j+k]=(x+y)%Mod;
					f[i+j+k]=(x-y+Mod)%Mod;
				}
			}
		}
		if(type==-1){
			int x=Pow(lim,Mod-2);
			rep(i,0,lim-1) f[i]=1LL*f[i]*x%Mod;
		}
	}
}
using Fourier::Getrev;
using Fourier::NTT;
Poly operator +(Poly a,Poly b){
	int n=b.size(); a.resize(max(a.size(),b.size()));
	rep(i,0,n-1) a[i]=(a[i]+b[i])%Mod;
	return a;
}
Poly operator -(Poly a,Poly b){
	int n=b.size(); a.resize(max(a.size(),b.size()));
	rep(i,0,n-1) a[i]=(a[i]+Mod-b[i])%Mod;
	return a;
}
Poly operator *(Poly a,int b){
	int n=a.size();
	rep(i,0,n-1) a[i]=1ll*a[i]*b%Mod;
	return a;
}
Poly operator *(Poly a,Poly b){
	if(a.empty()||b.empty()) return Poly();
	int n=a.size()+b.size()-1,lim=Getrev(n);
	a.resize(lim); b.resize(lim);
	NTT(&a[0],lim,1),NTT(&b[0],lim,1);
	rep(i,0,lim-1) a[i]=1ll*a[i]*b[i]%Mod;
	NTT(&a[0],lim,-1);
	return resize(a,n);
}
Poly operator <<(Poly a,int x){
	int n=a.size()+x; a.resize(n);
	per(i,n-1,x) a[i]=a[i-x];
	rep(i,0,x-1) a[i]=0;
	return a;
}
Poly operator >>(Poly a,int x){
	int n=a.size()-x;
	rep(i,0,n-1) a[i]=a[i+x];
	return resize(a,n);
}
Poly Inv(Poly a){
	if(a.size()==1) return Poly(1,Pow(a[0],Mod-2));
	int n=a.size(),lim;
	Poly b=Inv(resize(a,n+1>>1));
	lim=Getrev(n+2*b.size());
	Poly c=resize(b,lim),d=resize(a,lim);
	NTT(&c[0],lim,1); NTT(&d[0],lim,1);
	rep(i,0,lim-1) c[i]=(2-1ll*c[i]%Mod*d[i]%Mod+Mod)%Mod*c[i]%Mod;
	NTT(&c[0],lim,-1);
	return resize(c,n); 
}
Poly Derivative(Poly a){
	int n=a.size();
	rep(i,1,n-1) a[i-1]=1ll*a[i]*i%Mod;
	return resize(a,n-1);
}
Poly Integral(Poly a){
	int n=a.size(); a.push_back(0);
	per(i,n,1) a[i]=1ll*a[i-1]*Pow(i,Mod-2)%Mod;
	a[0]=0;
	return a;
}
Poly Ln(Poly a){ return Integral(resize(Derivative(a)*Inv(a),(int)a.size()-1)); }
Poly Exp(Poly a){
	if(a.size()==1) return Poly(1,1);
	int n=a.size();
	Poly b=Exp(resize(a,n+1>>1));
	return resize(b+b*(a-Ln(resize(b,n))),n);
}
Poly operator /(Poly a,Poly b){
	int n=a.size(),m=b.size();
	if(n<m) return Poly();
	reverse(a.begin(),a.end());
	reverse(b.begin(),b.end());
	a=resize(resize(a,n-m+1)*Inv(resize(b,n-m+1)),n-m+1);
	reverse(a.begin(),a.end());
	return a;
}
Poly operator %(Poly a,Poly b){ return resize(a-a/b*b,(int)b.size()-1); }
Poly Pow(Poly a,int k){
	int p=0,n=a.size();
	for(;p<n&&!a[p];p++);
	if(1ll*p*k>=n) return Poly(n);
	Poly b; rep(i,p,n-1) b.push_back(a[i]);
	int w=b[0],inv=Pow(w,Mod-2),wk=Pow(w,k);
	b=Exp(Ln(b*inv)*k)*wk;
	Poly c(p*k+(int)b.size());
	rep(i,0,(int)b.size()-1) c[i+p*k]=b[i];
	return resize(c,n);
}
namespace Eval_Inter{
	const int N=(1<<20)+5;
	int n; Poly prod[N],f;
	typedef vector<int> vi;
	vi x,y;
	void Getprod(int p,int l,int r){
		if(l==r){
			prod[p].resize(2);
			prod[p][0]=Mod-x[l],prod[p][1]=1;
			return ;
		}
		int mid=l+r>>1;
		Getprod(p<<1,l,mid); Getprod(p<<1|1,mid+1,r);
		prod[p]=prod[p<<1]*prod[p<<1|1];
	}
	void Eval(int p,int l,int r,Poly f){
		f=resize(f%prod[p],prod[p].size()-1);
		if(l==r) return (void)(y[l]=f[0]);
		int mid=l+r>>1;
		Eval(p<<1,l,mid,f); Eval(p<<1|1,mid+1,r,f);
	}
	vi Eval(Poly _f,vi _x){
		n=_x.size(),x=_x,f=_f;
		if(!n) return Poly();
		y.resize(n);
		Getprod(1,0,n-1);
		Eval(1,0,n-1,f);
		return y;
	}
	Poly Inter(int p,int l,int r){
		if(l==r) return Poly(1,y[l]);
		int mid=l+r>>1;
		return Inter(p<<1,l,mid)*prod[p<<1|1]+Inter(p<<1|1,mid+1,r)*prod[p<<1];
	}
	Poly Inter(vector<pair<int,int> >a){
		n=a.size(); y.resize(n); x.resize(n);
		if(!n) return Poly();
		rep(i,0,n-1) x[i]=a[i].first;
		Getprod(1,0,n-1);
		Poly m=Derivative(prod[1]);
		Eval(1,0,n-1,m);
		rep(i,0,n-1) y[i]=1ll*a[i].second*Pow(y[i],Mod-2)%Mod;
		return Inter(1,0,n-1);
	}
}
using Eval_Inter::Eval;
using Eval_Inter::Inter;

namespace Cipolla{
	typedef pair<int,int> pii;
	#define fir first
	#define sec second
	int w,t;
	pii operator *(pii a,pii b){
		int x=0,y=0;
		x=(1ll*a.fir*b.fir%Mod+1ll*a.sec*b.sec%Mod*w%Mod)%Mod;
		y=(1ll*a.fir*b.sec%Mod+1ll*a.sec*b.fir%Mod)%Mod;
		return make_pair(x,y);
	}
	pii PPow(pii x,int y){
		pii res=make_pair(1,0);
		for(;y;x=x*x,y>>=1) if(y&1) res=res*x;
		return res;
	}
	int Sqrt(int x){
		if(x==0) return 0;
		if(Pow(x,(Mod-1)/2)!=1) return -1;
		do{
			t=1ll*rand()*rand()%(Mod-1)+1;
			w=(1ll*t*t+Mod-x)%Mod;
		}while(Pow(w,(Mod-1)/2)==1);
		pii res=PPow(make_pair(t,1),(Mod+1)/2);
		return min(res.fir,Mod-res.fir);
	}
	#undef fir
	#undef sec
}
using Cipolla::Sqrt;
Poly Sqrt(Poly a){
	if(a.size()==1) return Poly(1,Sqrt(a[0]));
	int n=a.size();
	Poly b=resize(Sqrt(resize(a,n+1>>1)),n);
	return resize((b+a*Inv(b))*(Mod+1>>1),n);
}
Poly Sp_Mod(long long n,Poly &a){
	if(n<(int)a.size()-1){
		Poly res(n+1);
		res[n]=1;
		return res;
	}
	Poly b=Sp_Mod(n>>1,a);
	b=b*b;
	if(n&1) b=b<<1;
	return b%a;
}

//这些真的很很很难。。。感觉对于作者来讲基本上是不可能考场A这种题的。

FWT*

const int N=4e5+5,Mod=998244353,inv2=499122177;
int Cor[2][2]={{1,0},{1,1}},Cand[2][2]={{1,1},{0,1}},Cxor[2][2]={{1,1},{1,Mod-1}},
ICor[2][2]={{1,0},{Mod-1,1}},ICand[2][2]={{1,Mod-1},{0,1}},ICxor[2][2]={{inv2,inv2},{inv2,Mod-inv2}};
int n,a[N],b[N],f[N],g[N];
void FWT(int *f,int c[2][2]){
	for(int i=1;i<n;i<<=1)
		for(int j=0;j<n;j+=i<<1)
			for(int k=j;k<i+j;k++){
				long long x=f[k],y=f[k+i];
				f[k]=(c[0][0]*x%Mod+c[0][1]*y%Mod)%Mod;
				f[k+i]=(c[1][0]*x%Mod+c[1][1]*y%Mod)%Mod;
			}
}
void mul(int *f,int *g,int c[2][2],int Ic[2][2]){
	FWT(f,c); FWT(g,c);
	rep(i,0,n-1) f[i]=1ll*f[i]*g[i]%Mod;
	FWT(f,Ic);
}

和多项式全家桶一样QvQ

计算几何

凸包

struct point{
	double x,y;
	point operator -(point a){
		return (point){x-a.x,y-a.y};
	}
	double operator *(point a){
		return x*a.y-y*a.x;
	}
}p[N];
double dis(point a,point b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(point a,point b){
	int t=(a-p[1])*(b-p[1]);
	if(t==0) return dis(p[1],a)<dis(p[1],b);
	return t<0;
}
int n,top,st[N],tb[N],Top;
void graham(){
	int k=1;
	rep(i,2,n) if(p[k].y>p[i].y||(p[k].y==p[i].y&&p[k].x>p[i].x)) k=i;
	swap(p[1],p[k]);
	sort(p+2,p+n+1,cmp);
	st[++top]=1,st[++top]=2;
	rep(i,3,n){
		while(top>1&&(p[i]-p[st[top-1]])*(p[st[top]]-p[st[top-1]])<=0) top--;
		st[++top]=i;
	}
	st[top+1]=1;
	Top=top;
	rep(i,1,Top+1){tb[i]=st[i];}
}

旋转卡壳

waiting for update

半平面交

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
const double eps=1e-9; 
struct vec{
	double x,y;
	vec(){}
	vec(double _x,double _y):x(_x),y(_y){}
	bool operator<(vec a) const { return abs(x-a.x)<eps?y<a.y:x<a.x; }
	bool operator==(vec a) const { return abs(x-a.x)<eps&&abs(y-a.y)<eps; }
	bool operator!=(vec a) const { return !(*this==a); }
	vec operator+(vec a) const { return vec(x+a.x,y+a.y); }
	vec operator-(vec a) const { return vec(x-a.x,y-a.y); }
	double operator*(vec a) const { return x*a.y-y*a.x; }
	double operator^(vec a) const { return x*a.x+y*a.y; }
	vec operator*(double a) const { return vec(a*x,a*y); }
	double length(){ return sqrt(x*x+y*y); }
	friend double length(vec a){ return a.length(); }
}b[N],h[N];
struct line{
	vec s,t;
	line(){}
	line(vec _s,vec _t):s(_s),t(_t){}
	friend vec cross(line a,line b){
		vec u=a.s-b.s,v=a.t-a.s,w=b.t-b.s;
		double t=w*u/(v*w);
		return a.s+v*t;
	}
	friend bool isleft(line l,vec x){ return (l.t-l.s)*(x-l.s)>eps; }
	bool operator<(line a) const {
		vec u=t-s,v=a.t-a.s;
		double t1=atan2(u.y,u.x),t2=atan2(v.y,v.x);
		if(abs(t1-t2)>eps) return t1<t2;
		return isleft(a,s);
	}
	bool operator==(line a) const {
		vec u=t-s,v=a.t-a.s;
		double t1=atan2(u.y,u.x),t2=atan2(v.y,v.x);
		return abs(t1-t2)<eps;
	}
}a[N],q[N];
int n,m,tot; double ans=1e12;
void solve(int n){
	int l=1,r=0;
	rep(i,1,n){
		if(a[i]==a[i-1]) continue;
		while(r>l&&(!isleft(a[i],cross(q[r],q[r-1])))) r--;
		while(r>l&&(!isleft(a[i],cross(q[l],q[l+1])))) l++;
		q[++r]=a[i];
	}
	while(r>l&&(!isleft(q[l],cross(q[r],q[r-1])))) r--;
	while(r>l&&(!isleft(q[r],cross(q[l],q[l+1])))) l++;
	rep(i,l,r-1) h[++tot]=cross(q[i],q[i+1]);
}

上述是几个不好记的(题主比较菜),有一些也不好推着写的。如果能推着写的里面都有注释,应当每天都看看争取早日背过,或者加深过程理解印象。标*的实用难度较大,根据个人情况适当弃疗。

并且还存在一些应该复习的东西,高斯消元,矩阵树定理,卢卡斯定理,AC自动机,拓欧,点双,边双,欧拉回路 等等,不过由于记忆并不是特别的困难并且原理有部分比较容易明白,视情况复习。

posted @ 2020-06-08 16:16  Atoner  阅读(388)  评论(0编辑  收藏  举报