因为作者又懒又菜还健忘,所以要整理一下模板。
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自动机,拓欧,点双,边双,欧拉回路 等等,不过由于记忆并不是特别的困难并且原理有部分比较容易明白,视情况复习。