• 模板整理


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

    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 几个核心科技

    void make_root(int p){
    	access(p),splay(p),tree[p].tag^=1;
    }
    int find_root(int p){
    	access(p),splay(p);
    	for(push_down(p);son(p,0);push_down(p=son(p,0)));
    	return p;
    }
    void split(int x,int y){
    	make_root(x),access(y),splay(y);
    }//split后x,y的链独立成平衡树
    void link(int x,int y){
    	make_root(x);
    	if(find_root(y)!=x) tree[x].fa=y;
    	upd(y);
    }
    void cut(int x,int y){
    	make_root(x);
    	if(find_root(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_iprod_{j ot=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]);
    

    多项式全家桶*

    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自动机,拓欧,点双,边双,欧拉回路 等等,不过由于记忆并不是特别的困难并且原理有部分比较容易明白,视情况复习。

  • 相关阅读:
    DHCP配置实例
    upupw phpmyadmin写shell
    网络配置课学期总结
    c#写一个网站后台扫描器
    移位运算符
    JavaScript 事件
    JS自动爆炸案例
    生成树协议
    暴力操作节点
    为博客园添加统计访问量的工具
  • 原文地址:https://www.cnblogs.com/Atoner/p/13066370.html
Copyright © 2020-2023  润新知