发呆用
Graph
缩点
点击查看代码
vector<int>e[N];
int dfn[N],low[N],tim;
int st[N],top;bool in[N];
int bl[N],scc;
void tarjan(int u){
dfn[u]=low[u]=++tim;
st[++top]=u,in[u]=true;
for(int v:e[u]){
if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
else if(in[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
scc++;
for(int x=0;x!=u;)
x=st[top--],in[x]=false,bl[x]=scc;
}
}
2-SAT 差不多。
割点
点击查看代码
vector<int>e[N];
int dfn[N],low[N],tim;
bool cut[N];
void tarjan(int u,int rt){
dfn[u]=low[u]=++tim;
int fl=0;
for(int v:e[u]){
if(!dfn[v]){
tarjan(v,rt),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])fl++;
}
else low[u]=min(low[u],dfn[v]);
}
if(fl>1||(fl&&u!=rt))cut[u]=true;
}
点双
点击查看代码
int dfn[N],low[N],tim;
int st[N],top;
vector<int>s[N];int scc;
void tarjan(int u,int E){
dfn[u]=low[u]=++tim;
if(!E&&!head[u]){
s[++scc].push_back(u);
return;
}
st[++top]=u;
for(int i=head[u],v;i;i=nxt[i]){
if(i==(E^1))continue;
if(!dfn[v=ver[i]]){
tarjan(v,i),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
scc++;
for(int x=0;x!=v;)
x=st[top--],s[scc].push_back(x);
s[scc].push_back(u);
}
}
else low[u]=min(low[u],dfn[v]);
}
}
边双
点击查看代码
int dfn[N],low[N],tim;bool bri[M<<1];
vector<int>s[N];int scc;
void tarjan(int u,int E){
dfn[u]=low[u]=++tim;
for(int i=head[u],v;i;i=nxt[i]){
if(i==(E^1))continue;
if(!dfn[v=ver[i]]){
tarjan(v,i),low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])bri[i]=bri[i^1]=true;
}
else low[u]=min(low[u],dfn[v]);
}
}
int bl[N];
void dfs(int u){
bl[u]=scc,s[scc].push_back(u);
for(int i=head[u],v;i;i=nxt[i]){
if(bl[v=ver[i]]||bri[i])continue;
dfs(v);
}
}
欧拉路径
点击查看代码
int cur[N];
stack<int>q;
void dfs(int x){
for(int i=cur[x];i<e[x].size();i=cur[x])
cur[x]++,dfs(e[x][i]);
q.push(x);
}
Main
bool flag=true;
for(int i=1;i<=n;i++){
if(in[i]==out[i])continue;
flag=false;
if(out[i]==in[i]+1)c1++,st=i;
else if(in[i]==out[i]+1)c2++;
else printf("No\n"),exit(0);
}
if(!flag&&(c1!=1||c2!=1))
printf("No\n"),exit(0);
dfs(st);
while(!q.empty())
printf("%d ",q.top()),q.pop();
printf("\n");
SPFA
点击查看代码
int n,m,cnt[N];
int dis[N];bool in[N];
vector<pii>e[N];
bool spfa(){
memset(dis,0x3f,sizeof(dis));
queue<int>q;
dis[1]=0,q.push(1);
while(!q.empty()){
int u=q.front();q.pop();
cnt[u]++,in[u]=false;
for(pii E:e[u]){
if(dis[E.fi]>dis[u]+E.se){
dis[E.fi]=dis[u]+E.se;
if(!in[E.fi]){
q.push(E.fi);
if(++cnt[E.fi]>=n)return true;
}
}
}
}
return false;
}
匈牙利
Match
bool dfs(int x){
for(int y:e[x])
if(!vis[y]){
vis[y]=true;
if(!match[y]||dfs(match[y]))
return match[y]=x,true;
}
return false;
}
Main
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
ans+=dfs(i);
}
先染色然后遍历左部点。
最大流
Flow
int head[N],ver[M],nxt[M];ll edge[M];int tot;
void add(int u,int v,ll w){
nxt[++tot]=head[u],ver[tot]=v,edge[tot]=w,head[u]=tot;
nxt[++tot]=head[v],ver[tot]=u,edge[tot]=0,head[v]=tot;
}
int d[N],now[N];ll flow;
bool bfs(){
memset(d,0,sizeof(d));
queue<int>q;
q.push(S),d[S]=1,now[S]=head[S];
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x],y;i;i=nxt[i]){
if(edge[i]&&!d[y=ver[i]]){
now[y]=head[y],d[y]=d[x]+1,q.push(y);
if(y==T)return true;
}
}
}
return false;
}
ll dinic(int x,ll flow){
if(x==T)return flow;
ll ret=0;
for(int i=now[x],y;i&&flow;i=nxt[i]){
now[x]=i,y=ver[i];
if(edge[i]&&d[y]==d[x]+1){
ll k=dinic(y,min(flow,edge[i]));
if(!k)d[y]=0;
edge[i]-=k,edge[i^1]+=k;
ret+=k,flow-=k;
}
}
return ret;
}
Main
while(bfs()){
while(flow=dinic(S,inf))
ans+=flow;
}
费用流
点击查看代码
int n,m,S,T;
int head[N],ver[M],nxt[M],w[M],c[M],tot;
void add(int u,int v,int flow,int cost){
nxt[++tot]=head[u],ver[tot]=v,w[tot]=flow,c[tot]=cost,head[u]=tot;
nxt[++tot]=head[v],ver[tot]=u,w[tot]=0,c[tot]=-cost,head[v]=tot;
}
int dis[N],incf[N],pre[N];bool vis[N];
int MX,mf,mc;
bool SPFA(){
queue<int>q;
memset(dis,0x3f,sizeof(dis)),MX=dis[0];
memset(vis,0,sizeof(vis));
q.push(S),dis[S]=0,vis[S]=true,incf[S]=MX;
while(!q.empty()){
int u=q.front();q.pop(),vis[u]=false;
for(int i=head[u],v;i;i=nxt[i]){
if(!w[i])continue;
if(dis[v=ver[i]]>dis[u]+c[i]){
dis[v]=dis[u]+c[i];
incf[v]=min(incf[u],w[i]),pre[v]=i;
if(!vis[v])vis[v]=true,q.push(v);
}
}
}
return dis[T]!=MX;
}
void MCMF(){
while(SPFA()){
mf+=incf[T],mc+=dis[T]*incf[T];
for(int x=T,i=pre[x];x!=S;){
w[i]-=incf[T],w[i^1]+=incf[T];
x=ver[i^1],i=pre[x];
}
}
}
String
KMP
点击查看代码
int kmp[N];
void KMP(char *s,int len){
for(int i=2,j=0;i<=len;i++){
while(j&&s[i]!=s[j+1])j=kmp[j];
if(s[i]==s[j+1])j++;
kmp[i]=j;
}
}
int len;char s[N];
int main(){
scanf("%s",s+1),len=strlen(s+1);
KMP(s,len);
for(int i=1;i<=len;i++)
printf("%d ",kmp[i]);
return 0;
}
manacher
点击查看代码
int n;char t[N],s[N<<1];
int len[N<<1];
void getstr(){
int m=0;s[m++]='@';
for(int i=1;i<=n;i++)
s[m++]='#',s[m++]=t[i];
s[m++]='#',s[n=m]=0;
}
int manacher(){
int mx=0,id,mxlen=0;
for(int i=1;i<n;i++){
if(mx>i)len[i]=min(mx-i,len[2*id-i]);
else len[i]=1;
while(s[i+len[i]]==s[i-len[i]])len[i]++;
if(len[i]+i>mx){
mx=len[i]+i,id=i;
mxlen=max(mxlen,len[i]);
}
}
return mxlen-1;
}
SAM
看看板子。
SAM
struct state{
int len,link,siz;
int nxt[26];
#define len(x) st[x].len
#define link(x) st[x].link
#define siz(x) st[x].siz
#define nxt(x,c) st[x].nxt[c]
}st[N];
int node,last;
void init(){
st[1].len=0,st[1].link=0;
node=last=1;
}
void extend(int c){
int cur=++node;
len(cur)=len(last)+1,siz(cur)=1;
int p=last;
while(p&&!nxt(p,c))
nxt(p,c)=cur,p=link(p);
if(!p)link(cur)=1;
else{
int q=nxt(p,c);
if(len(p)+1==len(q))link(cur)=q;
else{
int tp=++node;
st[tp]=st[q],siz(tp)=0,len(tp)=len(p)+1;
while(p&&nxt(p,c)==q)
nxt(p,c)=tp,p=link(p);
link(q)=link(cur)=tp;
}
}
last=cur;
}
拓扑
scanf("%s",s+1),n=strlen(s+1);
init();
for(int i=1;i<=n;i++)extend(s[i]-'a');
for(int i=1;i<=node;i++)t[len(i)]++;
for(int i=1;i<=node;i++)t[i]+=t[i-1];
for(int i=1;i<=node;i++)A[t[len(i)]--]=i;
for(int i=node;i>1;i--){
int now=A[i];
if(link(now))siz(link(now))+=siz(now);
if(siz(now)>1)ans=max(ans,1ll*len(now)*siz(now));
}
Math
原根
\(m\) 有原根:\(m=2,4,p^k,2p^k\)。
原根判定定理:\(\forall p\in \mathrm{P},p\mid \varphi(m)\),\(g^{\frac{\varphi(m)}{p}}\ne 1\pmod m\)。
Pollard-Rho
点击查看代码
mt19937 rd(233);
ll read(){
ll x=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x;
}
ll qmul(ll k,ll b,ll p){
return (__int128)k*b%p;
}
ll qpow(ll k,ll b,ll p){
ll val=1;k%=p;
while(b){
if(b&1)val=qmul(val,k,p);
k=qmul(k,k,p),b>>=1;
}
return val;
}
ll T,n,ans;
bool check(ll a,ll n){
ll u=n-1,t=0;
while(!(u&1))u>>=1,t++;
ll x=qpow(a,u,n);
if(x==1)return false;
for(ll i=1,tmp;i<=t;i++,x=tmp){
tmp=qmul(x,x,n);
if(x!=1&&x!=n-1&&tmp==1)
return true;
}
return x!=1;
}
bool Miller_Rabin(ll n){
if(n==2)return true;
if(n<2||!(n&1))return false;
for(ll i=1;i<=Test;i++){
if(check(rd()%(n-1)+1,n))
return false;
}
return true;
}
ll f(ll x,ll c,ll n){
return (qmul(x,x,n)+c)%n;
}
ll Pollard_Rho(ll n){
if(!(n&1))return 2;
ll x=rd()%(n-1)+1,y=x,c=rd()%(n-1)+1,ret;
for(ll i=1;;i<<=1,y=x,ret=1){
for(ll j=1;j<=i;j++){
x=f(x,c,n);
ret=qmul(ret,abs(x-y),n);
if(!(j&127)){
ll d=__gcd(ret,n);
if(d>1)return d;
}
}
ll d=__gcd(ret,n);
if(d>1)return d;
}
}
Burnside
\[L=\frac{1}{|G|}\Big(c_1(a_1)+c_1(a_2)+\cdots+c_1(a_g)\Big)
\]
\(c_1(a_k)\) 理解为不变方案数即可。
BSGS
点击查看代码
ll BSGS(ll a,ll b,ll p){
map<ll,ll>hs;
hs.clear(),b%=p;
ll B=sqrt(p)+1;
for(ll i=0,cur=b;i<B;i++)
hs[cur]=i,(cur*=a)%=p;
a=qpow(a,B,p);
if(!a)return !b?1:-1;
for(ll i=1,cur=1;i<=B;i++){
(cur*=a)%=p;
ll j=hs.find(cur)==hs.end()?-1:hs[cur];
if(j>=0&&i*B-j>=0)return i*B-j;
}
return -1;
}
CRT
令 \(M=\prod m_i\),\(M_i=\dfrac{M}{m_i}\),\(t_i\equiv M_i^{-1}\pmod {m_i}\)。
一解为 \(\displaystyle x=\sum_{i=1}^{k}a_iM_it_i\),任意解 \(x_0=x+k\cdot M\)。
FWT
点击查看代码
void OR(int *a,int x){
for(int p=2,k=1;p<=lim;p<<=1,k<<=1)
for(int i=0;i<lim;i+=p)
for(int j=0;j<k;j++)
(a[i+j+k]+=1ll*x*a[i+j]%P)%=P;
}
void AND(int *a,int x){
for(int p=2,k=1;p<=lim;p<<=1,k<<=1)
for(int i=0;i<lim;i+=p)
for(int j=0;j<k;j++)
(a[i+j]+=1ll*x*a[i+j+k]%P)%=P;
}
void XOR(int *a,int x){
for(int p=2,k=1;p<=lim;p<<=1,k<<=1)
for(int i=0;i<lim;i+=p)
for(int j=0;j<k;j++){
(a[i+j]+=a[i+j+k])%=P;
a[i+j+k]=((a[i+j]-a[i+j+k]*2)%P+P)%P;
a[i+j]=1ll*a[i+j]*x%P,a[i+j+k]=1ll*a[i+j+k]*x%P;
}
}
子集卷积
一个观察是 \(|i|+|j|=|i\cup j|\)。
点击查看代码
void OR(int *a,int x){
for(int p=2,k=1;p<=lim;p<<=1,k<<=1)
for(int i=0;i<lim;i+=p)
for(int j=0;j<k;j++)
(a[i+j+k]+=1ll*a[i+j]*x%P)%=P;
}
int f[N][M],g[N][M],h[N][M];
int main(){
n=read(),lim=(1<<n);
for(int i=0;i<lim;i++)f[pcnt(i)][i]=read();
for(int i=0;i<lim;i++)g[pcnt(i)][i]=read();
for(int i=0;i<=n;i++)
OR(f[i],1),OR(g[i],1);
for(int i=0;i<=n;i++)
for(int j=0;j+i<=n;j++)
for(int s=0;s<(1<<n);s++)
(h[i+j][s]+=1ll*f[i][s]*g[j][s]%P)%=P;
for(int i=0;i<=n;i++)
OR(h[i],P-1);
for(int i=0;i<lim-1;i++)
printf("%d ",h[pcnt(i)][i]);
printf("%d\n",h[n][lim-1]);
return 0;
}
多项式
别惦记你那多项式了。
点击查看代码
int qpow(int k,int b){
int ret=1;
while(b){
if(b&1)ret=1ll*ret*k%P;
k=1ll*k*k%P,b>>=1;
}
return ret;
}
int A[N],a2[N],lnb[N];
int lim,r[N],gn,tp,inv;
int iv[N];
void init_iv(int n){
iv[1]=1;
for(int i=2;i<=n;i++)
iv[i]=1ll*(P-P/i)*iv[P%i]%P;
}
void init_r(int lim){
for(int i=0;i<lim;i++)
r[i]=(r[i>>1]>>1)+(i&1)*(lim>>1);
}
void ntt(int *x,int opt){
for(int i=0;i<lim;i++)
if(r[i]<i)swap(x[i],x[r[i]]);
for(int p=2,k=1;p<=lim;p<<=1,k<<=1){
gn=qpow(3,(P-1)/p);
for(int i=0;i<lim;i+=p){
for(int j=0,g=1;j<k;j++,g=1ll*g*gn%P){
tp=1ll*x[i+j+k]*g%P;
x[i+j+k]=(x[i+j]-tp+P)%P;
x[i+j]=(x[i+j]+tp)%P;
}
}
}
if(opt==-1){
reverse(x+1,x+lim),inv=qpow(lim,P-2);
for(int i=0;i<lim;i++)
x[i]=1ll*x[i]*inv%P;
}
}
void Inv(int *a,int *b,int n){
if(n==1){
b[0]=qpow(a[0],P-2);
return;
}
Inv(a,b,(n+1)>>1);
lim=1;while(lim<(n<<1))lim<<=1;
init_r(lim);
for(int i=0;i<lim;i++)A[i]=i<n?a[i]:0;
ntt(A,1),ntt(b,1);
for(int i=0;i<lim;i++)
b[i]=1ll*b[i]*((2ll-1ll*A[i]*b[i]%P+P)%P)%P;
ntt(b,-1);
for(int i=n;i<lim;i++)b[i]=0;
}
void Ln(int *a,int *b,int n){
for(int i=0;i<(n<<2);i++)b[i]=0;
Inv(a,b,n);
lim=1;while(lim<(n<<1))lim<<=1;
init_r(lim);
for(int i=0;i<n-1;i++)
a2[i]=1ll*a[i+1]*(i+1)%P;
for(int i=n-1;i<lim;i++)a2[i]=0;
ntt(a2,1),ntt(b,1);
for(int i=0;i<lim;i++)
b[i]=1ll*b[i]*a2[i]%P;
ntt(b,-1);
for(int i=n-1;i;i--)
b[i]=1ll*b[i-1]*iv[i]%P;
for(int i=n;i<lim;i++)b[i]=0;
b[0]=0;
}
void Exp(int *a,int *b,int n){
if(n==1){
b[0]=1;
return;
}
Exp(a,b,(n+1)>>1);
Ln(b,lnb,n);
lim=1;while(lim<(n<<1))lim<<=1;
init_r(lim);
for(int i=0;i<n;i++)
lnb[i]=(a[i]-lnb[i]+P)%P;
for(int i=n;i<lim;i++)lnb[i]=b[i]=0;
lnb[0]++;
ntt(lnb,1),ntt(b,1);
for(int i=0;i<lim;i++)
b[i]=1ll*b[i]*lnb[i]%P;
ntt(b,-1);
for(int i=n;i<lim;i++)b[i]=0;
}
其他
笛卡尔树
小根堆,编号满足 BST 性质。
点击查看代码
for(int i=1;i<=n;i++){
while(tp&&a[st[tp]]>a[i])ls[i]=st[tp--];
if(st[tp])rs[st[tp]]=i;
st[++tp]=i;
}