目录
退役选手表示在近一两年不会更新板子,可能大学会捡起来这个博客吧
离Noip也不远了,最后\(30\)天,每天早上都会敲一敲板子.
随机放出.(大家也能看一看 qwq.
并查集
#include<cstdio>
#define R register
using namespace std;
int n,m,f[10008];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
scanf("%d%d",&n,&m);
for(R int i=1;i<=n;i++)f[i]=i;
for(R int i=1,opt,a,b;i<=m;i++)
{
scanf("%d%d%d",&opt,&a,&b);
if(opt==1)
{
R int fa=find(a),fb=find(b);
f[fa]=fb;
}
else
{
R int fa=find(a),fb=find(b);
puts(fa==fb?"Y":"N");
}
}
}
LIS
最长单调上升序列。
其他类型的可以改变一下符号。
#include<cstdio>
#include<algorithm>
#include<iostream>
#define R register
using namespace std;
inline void in(R int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int stk[100008],top,n;
int main()
{
in(n);
for(R int i=1,x;i<=n;i++)
{
in(x);
if(stk[top]<x)stk[++top]=x;
else
{
R int l=1,r=top;
while(l<=r)
{
R int mid=(l+r)>>1;
if(stk[mid]>x)r=mid-1;
else l=mid+1;
}
stk[l]=x;
}
}
printf("%d\n",top);
}
LCS
最长公共子序列。
如果不等,状态转移与前面存在的三种状态取\(max\).
可以滚动数组。
当然也可以树状数组优化。
这个优化必须要求:两个串均为全排列的某一种
不过的确懒得写了.
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define R register
using namespace std;
const int gz=1e4+5;
char a[gz],b[gz];
int la,lb,f[2][gz];
int main()
{
scanf("%s",a+1);scanf("%s",b+1);
la=strlen(a+1),lb=strlen(b+1);
for(R int i=1;i<=la;i++)
{
R int op=i&1;
for(R int j=1;j<=lb;j++)
{
if(a[i]==b[j])
f[op][j]=f[op^1][j-1]+1;
else f[op][j]=max(max(f[op^1][j],f[op][j-1]),f[op^1][j-1]);
}
}
printf("%d\n",f[la&1][lb]);
}
有理数取余
给出一个有理数\(c=\frac{a}{b}\),求\(c\ \bmod 19260817\)的值。
把快读稍作模改即可.
#include<bits/stdc++.h>
#define R register
#define mod 19260817
using namespace std;
long long a,b,ans;
inline void in(long long &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10%mod+(s-'0')%mod;s=getchar();}
x*=f;
}
inline long long ksm(long long x,long long p)
{
long long res=1;
for(;p;p>>=1,x=x*x%mod)
if(p&1)res=res*x%mod;
return res;
}
int main()
{
in(a),in(b);
if(b==0){printf("Angry!");return 0;}
ans=a*ksm(b,mod-2);
printf("%lld",(ans%mod+mod)%mod);
}
快速幂
注意这里要判断\(1^0 mod \ 1=0\)的情况.
#include<cstdio>
#define int long long
#define R register
using namespace std;
inline int ksm(int x,int y,int p)
{
if(y==0)return 1;
int res=1;
for(;y;y>>=1,x=x*x%p)
if(y&1)res=res*x%p;
return res%p;
}
int x,y,p;
signed main()
{
scanf("%lld%lld%lld",&x,&y,&p);
printf("%lld^%lld mod %lld=%lld",x,y,p,ksm(x,y,p)%p);
}
Nim游戏
判断奇异局势.
如果每一堆石子异或起来的值不为\(0\),那么先手必胜,
如果为\(0\),那么先手必败.
#include<cstdio>
#include<algorithm>
#include<iostream>
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int T,n;
int main()
{
in(T);
for(R int ans;T;T--)
{
in(n);ans=0;
for(R int i=1,x;i<=n;in(x),ans^=x,i++);
if(ans)puts("Yes");
else puts("No");
}
}
矩阵快速幂
难得第一次写重载,注意矩乘具有左结合右结合律的,\(ksm\)中别写反.
#include<cstdio>
#include<cctype>
#include<cstring>
#define int long long
#define R register
#define mod 1000000007
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m;
struct cod
{
int res[105][105];
cod(){memset(res,0,sizeof res);}
inline void nw()
{for(R int i=1;i<=n;i++)res[i][i]=1;}
cod friend operator *(const cod&a,const cod&b)
{
cod tmp;
for(R int k=1;k<=n;k++)
for(R int i=1;i<=n;i++)
for(R int j=1;j<=n;j++)
(tmp.res[i][j]+=a.res[i][k]*b.res[k][j])%=mod;
return tmp;
}
}ans,x;
inline void ksm(int y)
{
ans.nw();
for(;y;y>>=1,x=x*x)
if(y&1)ans=ans*x;
}
signed main()
{
in(n),in(m);
for(R int i=1;i<=n;i++)
for(R int j=1;j<=n;j++)
in(x.res[i][j]);
ksm(m);
for(R int i=1;i<=n;i++,puts(""))
for(R int j=1;j<=n;j++)
printf("%lld ",ans.res[i][j]);
}
线性基
查询最值.
开$long long \(!!,一定要开\)long long $!!!
PS:查询最小值的答案即为数组中最小的非零数。
查询最大值
#include<cstdio>
#include<algorithm>
#include<iostream>
#define int long long
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int p[64],ans,n;
inline void insert(R int x)
{
for(R int i=63;~i;i--)
{
if(x&(1LL<<i))
{
if(p[i])
x^=p[i];
else
{
p[i]=x;
break;
}
}
}
}
signed main()
{
in(n);
for(R int i=1,x;i<=n;i++)
in(x),insert(x);
for(R int i=63;~i;i--)
if((ans^p[i])>ans)
ans^=p[i];
printf("%lld",ans);
}
查询第\(k\)小
先将数组中的所有数变成包含这个最高位且可以通过\(Xor\)得到的最小值。
具体实现方法:
每一位向后扫,如果能变小就变小。
求解的时候就只要将\(k\)转化为二进制后,将其二进制所对应位置的数异或起来即可。
部分代码
inline int query(R int k)
{
R int tmp[64],res=0,cnt=0;
for(R int i=0;i<=63;i++)
{
for(R int j=i-1;j>=0;j--)
if(p[i]&(1LL<<j))p[i]^=p[j];
if(p[i])tmp[++cnt]=p[i];
}
for(R int i=1;i<=cnt;i++)
if(k&(1LL<<i))res^=tmp[i];
return res;
}
Lucas定理
实在懒得写了,就直接放一下好了,注意\(p\)必须是一个较小的质数.
#include<cstdio>
#include<cctype>
#define int long long
#define RI register int
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int T,fac[100008];
inline int ksm(int x,int y,int p)
{
int res=1;
for(;y;y>>=1,x=x*x%p)
if(y&1) res=x*res%p;
return res;
}
inline int C(int n,int m,int p)
{
if(m>n)return 0;
return ((ksm(fac[n-m],p-2,p)%p*ksm(fac[m],p-2,p)%p*fac[n])%p+p)%p;
}
int Lucas(int n,int m,int p)
{
if(m==0)return 1;
return (Lucas(n/p,m/p,p)%p*C(n%p,m%p,p)%p+p)%p;
}
signed main()
{
in(T);
fac[0]=fac[1]=1;
for(RI n,m,p;T;T--)
{
in(n),in(m),in(p);
for(RI i=2;i<=p;i++)fac[i]=fac[i-1]%p*i%p;
printf("%lld\n",(Lucas(n+m,m,p)+p)%p);
}
}
线性筛素数
顺便写了筛欧拉函数\(\phi\)
应该没锅 qwq
#include<cstdio>
#define R register
using namespace std;
int n,m,prime[100000008],tot,phi[100000008];
bool vis[100000008];
inline void pri()
{
vis[1]=true;
for(R int i=2;i<=n;i++)
{
if(!vis[i])prime[++tot]=i,phi[i]=i-1;
for(R int j=1;j<=tot and i*prime[j]<=n;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
}
}
int main()
{
scanf("%d%d",&n,&m);
pri();
for(R int x;m;m--)
{
scanf("%d",&x);
puts(vis[x]==0?"Yes":"No");
}
}
最小生成树
\(Kruskal\)做法 \(O(n\ logn)\)
#include<cstdio>
#include<algorithm>
#define N 5000
#define R register
using namespace std;
struct cod{
int u,v,w;
bool operator <(const cod&a)const
{
return w<a.w;
}
}edge[N*N+10];
int n,m,f[N],tot,cnt,ans;
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
scanf("%d%d",&n,&m);
for(R int i=1;i<=n;i++)f[i]=i;
for(R int i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
sort(edge+1,edge+m+1);
for(R int i=1;i<=m;i++)
{
int u=edge[i].u,v=edge[i].v,w=edge[i].w;
int fu=find(u),fv=find(v);
if(fu==fv)continue;
ans+=w;cnt++;f[fu]=fv;
if(cnt==n-1)break;
}
if(cnt==n-1)printf("%d",ans);
else printf("orz");
}
堆优化\(Prime\) \(O(n\ logn)\)
待补ing
最短路
\(Spfa\) \(O(ke)\)
k在大多数情况下接近2
#include<cstdio>
#include<queue>
#define N 500008
#define R register
using namespace std;
int n,m,s,dis[N],head[N],tot;
bool vis[N];
struct cod{int u,v,w;}edge[N];
inline void add(int x,int y,int z)
{
edge[++tot].u=head[x];
edge[tot].v=y;
edge[tot].w=z;
head[x]=tot;
}
inline void spfa()
{
for(R int i=1;i<=n;i++)dis[i]=2147483647;
queue<int>q;
q.push(s);vis[s]=true;dis[s]=0;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=false;
for(R int i=head[u];i;i=edge[i].u)
{
if(dis[edge[i].v]>dis[u]+edge[i].w)
{
dis[edge[i].v]=dis[u]+edge[i].w;
if(!vis[edge[i].v])
{
vis[edge[i].v]=true;
q.push(edge[i].v);
}
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(R int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
spfa();
for(R int i=1;i<=n;i++)printf("%d ",dis[i]);
}
\(SPFA\)判负环(BFS)
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
#define N 100086
#define clear(a) memset(a,0,sizeof a)
int n,m,T;
struct code{int u,v,w;}edge[N];
bool vis[N];
int head[N],tot,dis[N],cnt[N];
inline void add(int x,int y,int z)
{
edge[++tot].u=head[x];
edge[tot].v=y;
edge[tot].w=z;
head[x]=tot;
}
inline bool spfa(int now)
{
for(R int i=1;i<=n;i++)
vis[i]=false,dis[i]=2147483647,cnt[i]=false;
queue<int>q;q.push(now);
vis[now]=true;dis[now]=0;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=false;
if(cnt[u]>=n)return true;
for(R int i=head[u];i;i=edge[i].u)
{
if(dis[edge[i].v]>dis[u]+edge[i].w)
{
dis[edge[i].v]=dis[u]+edge[i].w;
if(!vis[edge[i].v])
{
q.push(edge[i].v);
vis[edge[i].v]=true;
cnt[edge[i].v]++;
if(cnt[edge[i].v]>=n)return true;
}
}
}
}
return false;
}
int main()
{
in(T);
while(T--)
{
in(n),in(m);
tot=0;clear(head);
for(R int i=1,u,v,w;i<=m;i++)
{
in(u),in(v),in(w);
if(w<0)add(u,v,w);
else add(u,v,w),add(v,u,w);
}
puts(spfa(1)?"YE5":"N0");
}
}
堆优化\(Dijkstra\) \(O((m+n) logn)\)
贪心策略.不能跑最长路
#include<cstdio>
#include<queue>
#define N 500008
#define R register
using namespace std;
int n,m,s,dis[N],head[N],tot;
bool vis[N];
struct cod{int u,v,w;}edge[N];
inline void add(int x,int y,int z)
{
edge[++tot].u=head[x];
edge[tot].v=y;
edge[tot].w=z;
head[x]=tot;
}
struct hop{
int u,d;
bool operator<(const hop&a)
const
{
return d>a.d;
}
};
inline void dijkstra()
{
for(R int i=1;i<=n;i++)dis[i]=2147483647;
priority_queue<hop>q;
q.push((hop){s,0});dis[s]=0;
while(!q.empty())
{
int u=q.top().u;q.pop();
if(vis[u])continue;
vis[u]=true;
for(R int i=head[u];i;i=edge[i].u)
{
if(dis[edge[i].v]>dis[u]+edge[i].w and !vis[edge[i].v])
{
dis[edge[i].v]=dis[u]+edge[i].w;
q.push((hop){edge[i].v,dis[edge[i].v]});
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(R int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
dijkstra();
for(R int i=1;i<=n;i++)printf("%d ",dis[i]);
}
树状数组
单点修改,区间查询.
超级短的树状数组 qwq
#include<cstdio>
#include<cctype>
#define lowbit(x) x&-x
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,tr[500008];
inline void add(int pos,int del){for(;pos<=n;pos+=lowbit(pos))tr[pos]+=del;}
inline int query(int pos){int res=0;for(;pos;pos-=lowbit(pos))res+=tr[pos];return res;}
int main()
{
in(n),in(m);
for(R int i=1,x;i<=n;i++)in(x),add(i,x);
for(R int opt,l,r;m;m--)
{
in(opt),in(l),in(r);
if(opt==1)add(l,r);
else printf("%d\n",query(r)-query(l-1));
}
}
区间修改,单点查询.
还是比较短 emmm
这里用到了差分思想,不会的可以看看我的这篇博客
#include<cstdio>
#include<cctype>
#define lowbit(x) x&-x
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,tr[500008],last;
inline void add(int pos,int del){for(;pos<=n;pos+=lowbit(pos))tr[pos]+=del;}
inline int query(int pos){int res=0;for(;pos;pos-=lowbit(pos))res+=tr[pos];return res;}
int main()
{
in(n),in(m);in(last);add(1,last);
for(R int i=2,x;i<=n;i++)
{
in(x);
add(i,x-last);
last=x;
}
for(R int opt,l,r,x;m;m--)
{
in(opt);
if(opt==1)in(l),in(r),in(x),add(l,x),add(r+1,-x);
else in(x),printf("%d\n",query(x));
}
}
线段树
其他类似单点修改,大同小异,这里给出区间修改,区间查询好了.
#include<cstdio>
#include<cctype>
#define ls o<<1
#define rs o<<1|1
#define int long long
#define N 100009
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,tr[N<<2],tg[N<<2];
inline void up(int o)
{
tr[o]=tr[ls]+tr[rs];
}
inline void down(int o,int l,int r)
{
if(tg[o])
{
tg[ls]+=tg[o];tg[rs]+=tg[o];
int mid=(l+r)>>1;
tr[ls]+=(mid-l+1)*tg[o];
tr[rs]+=(r-mid)*tg[o];
tg[o]=0;
}
}
void build(int o,int l,int r)
{
if(l==r)
{
in(tr[o]);
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
up(o);
}
void change(int o,int l,int r,int x,int y,int z)
{
if(x<=l and y>=r)
{
tr[o]+=(r-l+1)*z;
tg[o]+=z;
return;
}
down(o,l,r);
int mid=(l+r)>>1;
if(x<=mid)change(ls,l,mid,x,y,z);
if(y>mid)change(rs,mid+1,r,x,y,z);
up(o);
}
int query(int o,int l,int r,int x,int y)
{
if(x<=l and y>=r)return tr[o];
down(o,l,r);
int mid=(l+r)>>1,res=0;
if(x<=mid)res+=query(ls,l,mid,x,y);
if(y>mid)res+=query(rs,mid+1,r,x,y);
return res;
}
signed main()
{
in(n),in(m);build(1,1,n);
for(R int opt,x,y,z;m;m--)
{
in(opt);
if(opt==1){in(x),in(y),in(z),change(1,1,n,x,y,z);}
else{in(x),in(y);printf("%lld\n",query(1,1,n,x,y));}
}
}
扫描线
一.矩形面积并
重点在于去重操作和更新操作。
#include<cstdio>
#include<algorithm>
#define ls o<<1
#define rs o<<1|1
#define R register
using namespace std;
struct code
{
double l,r,h;
int f;
bool operator < (const code&a)const
{
return h<a.h;
}
}edge[2008];
struct cod
{
int l,r,s;
double len;
}tr[8888];
double x[2333];
void build(int o,int l,int r)
{
tr[o].l=l,tr[o].r=r;
tr[o].s=0;tr[o].len=0;
if(l==r)return;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
inline void up(int o)
{
if(tr[o].s)
tr[o].len=x[tr[o].r+1]-x[tr[o].l];
else if(tr[o].l==tr[o].r)
tr[o].len=0;
else tr[o].len=tr[ls].len+tr[rs].len;
}
void change(int o,int l,int r,int del)
{
if(tr[o].l==l && tr[o].r==r)
{
tr[o].s+=del;
up(o);
return;
}
int mid=(tr[o].l+tr[o].r)>>1;
if(r<=mid)change(ls,l,r,del);
else if(l>mid) change(rs,l,r,del);
else change(ls,l,mid,del),change(rs,mid+1,r,del);
up(o);
}
int main()
{
int n,cas=0;
for(;;)
{
scanf("%d",&n);
if(n==0)break;
int tot=0;
for(R int i=1;i<=n;i++)
{
R double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
edge[++tot].l=x1;edge[tot].r=x2;edge[tot].h=y1;
edge[tot].f=1;x[tot]=x1;
edge[++tot].l=x1;edge[tot].r=x2;edge[tot].h=y2;
edge[tot].f=-1;x[tot]=x2;
}
sort(x+1,x+tot+1);
sort(edge+1,edge+tot+1);
int new_n=1;
for(R int i=2;i<=tot;i++)
if(x[new_n]!=x[i])x[++new_n]=x[i];
build(1,1,new_n);
double ans=0;
for(R int i=1;i<=tot;i++)
{
int l=lower_bound(x+1,x+new_n+1,edge[i].l)-x;
int r=lower_bound(x+1,x+new_n+1,edge[i].r)-x-1;
change(1,l,r,edge[i].f);
ans+=(edge[i+1].h-edge[i].h)*tr[1].len;
}
printf("Test case #%d\n",++cas);
printf("Total explored area: %.2f\n\n",ans);
}
}
主席树.
维护静态区间第\(k\)大
#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 200008
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,a[N],b[N];
int cnt,sum[N*35],lson[N*35],rson[N*35],root[N*35];
void build(int lastroot,int &nowroot,int l,int r,int pos)
{
nowroot=++cnt;
sum[nowroot]=sum[lastroot]+1;
lson[nowroot]=lson[lastroot];
rson[nowroot]=rson[lastroot];
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)build(lson[lastroot],lson[nowroot],l,mid,pos);
else build(rson[lastroot],rson[nowroot],mid+1,r,pos);
}
int query(int lastroot,int nowroot,int l,int r,int pos)
{
if(l==r)return l;
int tmp=sum[lson[nowroot]]-sum[lson[lastroot]];
int mid=(l+r)>>1;
if(pos<=tmp)return query(lson[lastroot],lson[nowroot],l,mid,pos);
else return query(rson[lastroot],rson[nowroot],mid+1,r,pos-tmp);
}
int main()
{
in(n),in(m);
for(R int i=1;i<=n;i++)in(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int new_n=1;
for(R int i=2;i<=n;i++)
if(b[i]!=b[new_n])b[++new_n]=b[i];
for(R int i=1;i<=n;i++)
build(root[i-1],root[i],1,new_n,lower_bound(b+1,b+new_n+1,a[i])-b);
for(R int i=1,l,r,k;i<=m;i++)
{
in(l),in(r),in(k);
printf("%d\n",b[query(root[l-1],root[r],1,new_n,k)]);
}
}
ST表
这类题再不写快读,我自废双手好吧.
PS:不要用\(endl\)换行!
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cctype>
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int mx[100008][23],n,m;
int main()
{
in(n),in(m);
for(R int i=1;i<=n;i++)in(mx[i][0]);
for(R int j=1;j<=21;j++)
for(R int i=1;(i+(1<<j)-1)<=n;i++)
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
for(R int l,r,k;m;m--)
{
in(l),in(r);
k=log2(r-l+1);
printf("%d\n",max(mx[l][k],mx[r-(1<<k)+1][k]));
}
}
乘法逆元
线性求逆元
这个东西大家不应该都是临时手推嘛,为什么好多人都要背 emmm
假设我们当前枚举到了\(i\),这里\(p\)为模数
显然\(p\)可以写成这样
\[ p=k \times i+r\ \ \ (0\leq r<p) \]
则有
\[ k \times i+r \equiv 0 \ (mod \ p) \]
两边同时乘以\(r^{-1}\)和\(i^{-1}\)
\[ k \times r^{-1}+i^{-1} \equiv 0 \ (mod\ p) \]
移项。
\[ i^{-1} \equiv \ -k \times r^{-1} \ \ (mod \ p) \\ \]
而我们的\(k\)又等价于\(\lfloor \frac{p}{i} \rfloor\) ,\(r\)等价于\(p%i\)
因此原式为
\[ i^{-1}\equiv -1\times \lfloor \frac{p}{i} \rfloor \times[p%i ]^{-1} \ (mod\ p) \]
因为可能出负数,记得每次\(+p\)再\(%p\)即可
#include<cstdio>
#include<cctype>
#define int long long
#define R register
using namespace std;
int n,p,inv[3000008];
signed main()
{
scanf("%lld%lld",&n,&p);inv[1]=1;
for(R int i=2;i<=n;i++)
inv[i]=(-1*(p/i)*inv[p%i]+p)%p;
for(R int i=1;i<=n;i++)
printf("%lld\n",(inv[i]+p)%p);
}
快速幂求逆元
主要用到了费马小定理,这里就手推一下好了.
这是欧拉定理.
\[ a^{\phi(p)} \equiv 1 \ (mod \ p) \]
然后当\(p\)为质数的时候,\(\phi(p)=p-1\)
此时
\[ a^{p-1} \equiv {1}\ ( mod\ p) \]
稍作变形
\[ a^{p-2} \times a \equiv 1 \ (mod \ p) \]
此时\(a^{p-2}\)与\(a^{-1}\)等价,直接求即可。
最近公共祖先(LCA)
倍增算法,直接跳即可.
#include<cstdio>
#include<cctype>
#include<iostream>
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,s,depth[500008];
int head[500008],tot,f[500008][21];
struct cod{int u,v;}edge[1000008];
inline void add(int x,int y)
{
edge[++tot].u=head[x];
edge[tot].v=y;
head[x]=tot;
}
void dfs(int u,int fa)
{
depth[u]=depth[fa]+1;f[u][0]=fa;
for(R int i=1;(1<<i)<=depth[u];i++)
f[u][i]=f[f[u][i-1]][i-1];
for(R int i=head[u];i;i=edge[i].u)
{
if(edge[i].v==fa)continue;
dfs(edge[i].v,u);
}
}
inline int lca(int x,int y)
{
if(depth[x]>depth[y])swap(x,y);
for(R int i=17;i>=0;i--)
if(depth[x]+(1<<i)<=depth[y])
y=f[y][i];
if(x==y)return x;
for(R int i=17;i>=0;i--)
{
if(f[x][i]==f[y][i])continue;
x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int main()
{
in(n),in(m),in(s);
for(R int i=1,x,y;i<n;i++)
{
in(x),in(y);
add(x,y),add(y,x);
}
dfs(s,0);
for(R int i=1,x,y;i<=m;i++)
{
in(x),in(y);
printf("%d\n",lca(x,y));
}
}
树链剖分
这里放一下板子好了 emm.新学的 qwq.
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define int long long
#define R register
#define ls o<<1
#define rs o<<1|1
#define clear(a,b) memset(a,b,sizeof a)
#define N 100050
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,root,p,depth[N],size[N],f[N],top[N],dfn[N],fdfn[N];
int son[N],a[N],idx;
int head[N],tot,tr[N<<2],tg[N<<2];
struct cod{int u,v;}edge[N<<1];
inline void add(int x,int y)
{
edge[++tot].u=head[x];
edge[tot].v=y;
head[x]=tot;
}
inline void down(int o,int l,int r)
{
if(tg[o])
{
int mid=(l+r)>>1;
(tg[ls]+=tg[o])%=p;(tg[rs]+=tg[o])%=p;
(tr[ls]+=(mid-l+1)*tg[o])%=p;
(tr[rs]+=(r-mid)*tg[o])%=p;
tg[o]=0;
}
return;
}
void build(int o,int l,int r)
{
if(l==r)
{
tr[o]=a[fdfn[l]]%p;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
(tr[o]=tr[ls]+tr[rs])%=p;
}
void change(int o,int l,int r,int x,int y,int z)
{
if(x<=l and y>=r)
{
(tr[o]+=(r-l+1)*z)%=p;
(tg[o]+=z)%=p;
return;
}
down(o,l,r);
int mid=(l+r)>>1;
if(x<=mid)change(ls,l,mid,x,y,z);
if(y>mid)change(rs,mid+1,r,x,y,z);
(tr[o]=tr[ls]+tr[rs])%=p;
return;
}
int query(int o,int l,int r,int x,int y)
{
if(x<=l and y>=r) return tr[o];
down(o,l,r);
int mid=(l+r)>>1,res=0;
if(x<=mid)res+=query(ls,l,mid,x,y);
if(y>mid) res+=query(rs,mid+1,r,x,y);
return res;
}
void dfs1(int u,int fa)
{
depth[u]=depth[fa]+1;f[u]=fa;size[u]=1;
for(R int i=head[u];i;i=edge[i].u)
{
if(edge[i].v==fa)continue;
dfs1(edge[i].v,u);
size[u]+=size[edge[i].v];
if(son[u]==-1 or size[son[u]]<size[edge[i].v])
son[u]=edge[i].v;
}
}
void dfs2(int u,int t)
{
dfn[u]=++idx;fdfn[idx]=u;top[u]=t;
if(son[u]==-1)return;
dfs2(son[u],t);
for(R int i=head[u];i;i=edge[i].u)
{
if(dfn[edge[i].v])continue;
dfs2(edge[i].v,edge[i].v);
}
}
inline int tchange(int x,int y,int z)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(depth[fx]>depth[fy])
{
change(1,1,idx,dfn[fx],dfn[x],z);
x=f[fx];
}
else
{
change(1,1,idx,dfn[fy],dfn[y],z);
y=f[fy];
}
fx=top[x],fy=top[y];
}
if(dfn[x]>dfn[y])swap(x,y);
change(1,1,idx,dfn[x],dfn[y],z);
}
inline int tquery(int x,int y)
{
int fx=top[x],fy=top[y],ans=0;
while(fx!=fy)
{
if(depth[fx]>depth[fy])
{
(ans+=query(1,1,idx,dfn[fx],dfn[x]))%=p;
x=f[fx];
}
else
{
(ans+=query(1,1,idx,dfn[fy],dfn[y]))%=p;
y=f[fy];
}
fx=top[x],fy=top[y];
}
if(dfn[x]>dfn[y])swap(x,y);
(ans+=query(1,1,idx,dfn[x],dfn[y]))%=p;
return ans;
}
signed main()
{
in(n),in(m),in(root),in(p);
for(R int i=1;i<=n;i++)in(a[i]);
clear(son,-1);
for(R int i=1,x,y;i<n;i++)
{
in(x),in(y);
add(x,y),add(y,x);
}
dfs1(root,-1);dfs2(root,root);
build(1,1,idx);
for(R int i=1,opt,l,r,z;i<=m;i++)
{
in(opt);
switch(opt)
{
case 1:in(l),in(r),in(z),tchange(l,r,z);break;
case 2:in(l),in(r);printf("%lld\n",(tquery(l,r)+p)%p);break;
case 3:in(l),in(z);change(1,1,idx,dfn[l],dfn[l]+size[l]-1,z);break;
case 4:in(l);printf("%lld\n",(query(1,1,idx,dfn[l],dfn[l]+size[l]-1)+p)%p);break;
}
}
}
二分图匹配
一.匈牙利算法.
记得每次\(memset\).其他没有需要特别注意的。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,e,match[200008];
int head[200008],tot,ans;
struct cod{int u,v;}edge[200008*4];
inline void add(int x,int y)
{
edge[++tot].u=head[x];
edge[tot].v=y;
head[x]=tot;
}
bool vis[200008];
bool find(int x)
{
for(R int i=head[x];i;i=edge[i].u)
{
if(!vis[edge[i].v])
{
vis[edge[i].v]=1;
if(!match[edge[i].v] or find(match[edge[i].v]))
{
match[edge[i].v]=x;
return true;
}
}
}
return false;
}
int main()
{
in(n),in(m),in(e);
for(R int i=1,x,y;i<=e;i++)
{
in(x),in(y);
if(y>m)continue;
add(x,y);
}
for(R int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis);
if(find(i))ans++;
}
printf("%d",ans);
}
二.最大流(当前弧优化)
考虑到\(Noip\)不考,所以没再打一次,贴代码.
网络流也忘得差不多了
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define IL inline
#define RI register int
#define N 1500000+10
IL void read(int &x){
int f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
x*=f;
}
struct cod{int u,v,w;}edge[N];
int head[N],tot,s,t,depth[N],cur[N],n,m,e;
IL void add(int x,int y,int z){edge[++tot].u=head[x];edge[tot].v=y;edge[tot].w=z;head[x]=tot;}
IL bool bfs()
{
memset(depth,0,sizeof depth);
std::queue<int>q;
for(RI i=0;i<=n + m + 2;i++)cur[i]=head[i];
q.push(s);depth[s]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(RI i=head[u];i!=-1;i=edge[i].u)
{
if(!depth[edge[i].v]&&edge[i].w>0)
{
depth[edge[i].v]=depth[u]+1;
q.push(edge[i].v);
}
}
}
if(!depth[t])return false;return true;
}
IL int dfs(int u,int dist)
{
if(u==t || !dist)return dist;
int di=0,f;
for(RI i=cur[u];i!=-1;i=edge[i].u)
{
cur[u]=i;
if(depth[edge[i].v]==depth[u]+1 &&( f=dfs(edge[i].v,std::min(edge[i].w,dist))))
{
di+=f;dist-=f;
edge[i].w-=f;edge[i^1].w+=f;
if(!dist)break;
}
}
return di;
}
IL int dinic()
{
int ans=0;
while(bfs())
ans+=dfs(s,2147483647);
return ans;
}
int main()
{
memset(head,-1,sizeof head);tot=-1;
read(n),read(m),read(e);
s=0,t=n+m+1;
for(RI i=1,u,v,w;i<=e;i++){
read(u),read(v);
if(v<=m)
{
v+=n;
add(u,v,1),add(v,u,0);
}
}
for(RI i=1;i<=n;i++)add(s,i,1),add(i,s,0);
for(RI i=1;i<=m;i++)add(i+n,t,1),add(t,n+i,0);
printf("%d",dinic());
}
字符串
字符串哈希
这里用的是自然溢出哈希.才意识到跑这么快
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#define R register
#define base 131
using namespace std;
int n,ans=1;
unsigned long long a[10008];
inline unsigned long long has(char *s)
{
int len=strlen(s);
unsigned long long res=1;
for(R int i=0;i<len;i++)
res=res*base+(unsigned long long)s[i];
return res;
}
char s[1500];
int main()
{
scanf("%d",&n);
for(R int i=1;i<=n;i++)
scanf("%s",s),a[i]=has(s);
sort(a+1,a+n+1);
for(R int i=2;i<=n;i++)
if(a[i]!=a[i-1])ans++;
printf("%d",ans);
}
KMP看毛片
注意存储出现位置要写成\(i-lb+1\)
#include<cstdio>
#include<cstring>
#define R register
using namespace std;
char s1[1008611],s2[1008611];
int la,lb,nex[1008611],k,loc[1008611],cnt;
int main()
{
scanf("%s%s",s1+1,s2+1);
la=strlen(s1+1),lb=strlen(s2+1);
nex[1]=0;
for(R int i=2;i<=lb;i++)
{
while(k and s2[k+1]!=s2[i])k=nex[k];
if(s2[i]==s2[k+1])k++;
nex[i]=k;
}
k=0;
for(R int i=1;i<=la;i++)
{
while(k and s2[k+1]!=s1[i])k=nex[k];
if(s1[i]==s2[k+1])k++;
if(k==lb)
loc[++cnt]=i-lb+1;
}
for(R int i=1;i<=cnt;i++)
printf("%d\n",loc[i]);
for(R int i=1;i<=lb;i++)
printf("%d ",nex[i]);
}
Manacher
求\(S\)中最长回文串的长度.(非插入字符版
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define R register
using namespace std;
char ch[11000001];
char s[22000001];
int len,RL[22000001],MaxRight,center,ans;
int main()
{
scanf("%s",ch);
len=strlen(ch);
for(R int i=0;i<len;i++)s[2*i+1]=ch[i];
len=2*len+1;
for(R int i=0;i<len;i++)
{
if(i<=MaxRight)
RL[i]=min(RL[2*center-i],MaxRight-i);
else RL[i]=1;
while(s[i-RL[i]]==s[i+RL[i]] and i-RL[i]>=0 and i+RL[i]<len)
RL[i]++;
if(i+RL[i]-1>MaxRight)
MaxRight=i+RL[i]-1,center=i;
ans=max(ans,RL[i]-1);
}
printf("%d",ans);
}
AC自动机
出现的模式串的数量(就是求有多少短串在长串出现过)
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<iostream>
#define N 1000003
#define R register
using namespace std;
char s[N],keyword[N];
struct AC
{
int sz,ch[N][26],cnt[N],val[N],last[N],fail[N],num;
inline void init()
{
memset(ch[0],0,sizeof ch[0]);
memset(cnt,0,sizeof cnt[0]);
sz=1;
}
inline void insert(char *s)
{
int u=0;
for(R int i=0;s[i];i++)
{
int v=s[i]-'a';
if(!ch[u][v])
{
memset(ch[sz],0,sizeof ch[sz]);
val[sz]=0;
ch[u][v]=sz++;
}
u=ch[u][v];
}
val[u]++;
}
inline void fai()
{
fail[0]=0;
queue<int>q;
for(R int i=0;i<26;i++)
{
int u=ch[0][i];
if(u)
{
fail[u]=0;
q.push(u);
}
}
while(!q.empty())
{
int u=q.front();q.pop();
for(R int i=0;i<26;i++)
{
int v=ch[u][i];
if(!v)
{
ch[u][i]=ch[fail[u]][i];
continue;
}
q.push(v);
fail[v]=ch[fail[u]][i];
last[v]=val[fail[v]] ? fail[v]:last[fail[v]];
}
}
}
inline int count(char *s)
{
int u=0,res=0;
for(R int i=0;s[i];i++)
{
int v=s[i]-'a';
u=ch[u][v];
if(val[u])res+=val[u],val[u]=0;
int tmp=u;
while(last[tmp])
{
tmp=last[tmp];
if(val[tmp])res+=val[tmp],val[tmp]=0;
}
}
return res;
}
}ACs;
int main()
{
int n;scanf("%d",&n);ACs.init();
for(R int i=1;i<=n;i++)
{
cin>>keyword;
ACs.insert(keyword);
}
ACs.fai();cin>>s;
printf("%d\n",ACs.count(s));
}
输出单个模式串最多出现的次数,以及输出这个出现次数最多的模式串
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register
using namespace std;
const int maxn=1000003;
char keyword[153][71];
char s[1000006];
struct AC
{
int ch[maxn][26],val[maxn],cnt[maxn],last[maxn],fail[maxn];
int sz;
inline void init()
{
memset(ch[0],0,sizeof ch[0]);
memset(cnt,0,sizeof cnt);
sz=1;
}
inline void insert(R char *s,R int num)
{
R int u=0;
for(R int i=0;s[i];i++)
{
R int v=s[i]-'a';
if(!ch[u][v])
{
memset(ch[sz],0,sizeof ch[sz]);
val[sz]=0;
ch[u][v]=sz++;
}
u=ch[u][v];
}
val[u]=num;
}
inline void fai()
{
memset(fail,0,sizeof fail);
queue<int>q;fail[0]=0;
for(R int i=0;i<26;i++)
{
R int u=ch[0][i];
if(u)
{
fail[u]=0;
q.push(u);
}
}
while(!q.empty())
{
R int u=q.front();q.pop();
for(R int i=0;i<26;i++)
{
R int v=ch[u][i];
if(!v)
{
ch[u][i]=ch[fail[u]][i];
continue;
}
q.push(v);
fail[v]=ch[fail[u]][i];
last[v]=val[fail[v]] ? fail[v]:last[fail[v]];
}
}
}
void work(R int x)
{
if(x)
{
cnt[val[x]]++;
work(last[x]);
}
}
inline void query(R char *s)
{
R int u=0;
for(R int i=0;s[i];i++)
{
R int v=s[i]-'a';
if(!ch[u][v])u=fail[u];
while(u and !ch[u][v])u=fail[u];
u=ch[u][v];
if(val[u])work(u);
else if(last[u])work(last[u]);
}
}
}ac;
int main()
{
int T;
scanf("%d",&T);
while(T!=0)
{
R int ans=0;
ac.init();
for(R int i=1;i<=T;i++)
{
cin>>keyword[i];
ac.insert(keyword[i],i);
}
ac.fai();
cin>>s;
ac.query(s);
for(R int i=1;i<=T;i++)
if(ac.cnt[i]>ans)ans=ac.cnt[i];
printf("%d\n",ans);
for(R int i=1;i<=T;i++)
if(ac.cnt[i]==ans)
cout<<keyword[i]<<endl;
scanf("%d",&T);
}
}