Nickle的基础模板库!
前言
目录在右下角可打开。文章可能过长,建议使用目录浏览。
基本算法
前缀和
一维前缀和
二维前缀和
差分
一维差分
二维差分
排序
逆序对
CDQ分治
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int a[N],b[N],n;
long long ans;
void CDQ(int l,int r,int *a){
if(l==r) return ;
int mid=(l+r)>>1;
CDQ(l,mid,a);CDQ(mid+1,r,a);
int t1=l,t2=mid+1;
for(int i=l;i<=r;i++)
if(a[t1]>a[t2] && t2<=r || t1>mid) b[i]=a[t2++],ans+=mid-t1+1;
else b[i]=a[t1++];
for(int i=l;i<=r;i++)a[i]=b[i];
}
namespace Read{
template<typename T>
inline void read(T &x){
x=0;T f=1;char ch=getchar();
while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x*=f;
}
template <typename T, typename... Args>
inline void read(T& t, Args&... args) {
read(t); read(args...);
}
}
using namespace Read;
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
CDQ(1,n,a);
printf("%lld",ans);
return 0;
}
归并排序
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
int n,a[500005][2];
ll ans;
inline int read(){
char ch=getchar();
int x=0,f=1;
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
return x*f;
}
void guibing(int l,int r,int flag){
if(l==r) return;
int mid=l+r>>1;
guibing(l,mid,!flag); guibing(mid+1,r,!flag);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(a[i][flag]<=a[j][flag]) a[k++][!flag]=a[i++][flag];
else a[k++][!flag]=a[j++][flag],ans+=mid-i+1;
}
while(i<=mid) a[k++][!flag]=a[i++][flag];
while(j<=r) a[k++][!flag]=a[j++][flag];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) a[i][0]=a[i][1]=read();
guibing(1,n,0);
printf("%lld",ans);
}
贪心
微扰法
位运算
线性基
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
int n,base[67],a,ans=0;
//大概有些自己明白了
//n是个数,base是指每一位的基底,a是输入的数,ans是答案
void XXJ(int a){//每输入一个数
for(int j=51;~j;j--){//就查输入的数二进制下 最高位
if(a & (int)1<<j){//的 1
if(base[j]) {a^=base[j];}
//如果这一位已经有了,就异或这一位的数,继续查
//异或之后,这个数的最高位一定变小了
else {base[j]=a;break;}
//如果没有,就把数存入这一位
}
}
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){cin>>a;XXJ(a);}
for(int i=51;~i;i--){
if(base[i]) //如果这一位有数
ans=(ans>(base[i]^ans))?ans:(base[i]^ans);//考虑一个贪心
}
cout<<ans<<endl;
return 0;
}
图论
最短路
dijktra
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
namespace dij{
const int N=1e6+105;
const int INF=0x3f3f3f3f;
int h[N],cnt,d[N],to[N<<1],nex[N<<1],val[N<<1];
bool v[N];
struct qwq{
int dis,id;
bool operator < (const qwq &x)const{return x.dis<dis;}
};
priority_queue<qwq> q;
inline void add(int x,int y,int z){
to[++cnt]=y,val[cnt]=z;
nex[cnt]=h[x];h[x]=cnt;
}
int n,m,s;
inline void dijkstra(int s){
memset(d,INF,sizeof(d));
d[s]=0;q.push((qwq){0,s});
while(!q.empty()){
qwq tmp=q.top();q.pop();int x=tmp.id;
if(!v[x]){
v[x]=1;
for(int i=h[x];i;i=nex[i])
if(d[to[i]]>d[x]+val[i]){
d[to[i]]=d[x]+val[i];
if(!v[to[i]]) q.push((qwq){d[to[i]],to[i]});
}
}
}
}
}
using namespace dij;
int main(){
n=read();m=read();s=read();
for(int i=1,x,y,z;i<=m;i++){
x=read();y=read();z=read();
add(x,y,z);
}
dijkstra(s);
for(int i=1;i<=n;i++) printf("%d ",d[i]);
return 0;
}
spfa
spfa最短路
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
namespace spfa{
const int N=1e6+105;
const int INF=0x3f3f3f3f;
int h[N],cnt,d[N],to[N<<1],nex[N<<1],val[N<<1];
bool v[N];
inline void add(int x,int y,int z){
to[++cnt]=y,val[cnt]=z;
nex[cnt]=h[x];h[x]=cnt;
}
int n,m,s;
inline void SPFA(int s){
for(int i=1;i<=n;i++) d[i]=INF;
queue<int> q;d[s]=0;v[s]=1;q.push(s);
while(!q.empty()){
int x=q.front();q.pop();v[x]=0;
for(int i=h[x];i;i=nex[i]){
if(d[to[i]]>d[x]+val[i]){
d[to[i]]=d[x]+val[i];
if(!v[to[i]]){v[to[i]]=1;q.push(to[i]);}
}
}
}
}
}
using namespace spfa;
int main(){
n=read();m=read();s=read();
for(int i=1,x,y,z;i<=m;i++){
x=read();y=read();z=read();
add(x,y,z);
}
SPFA(s);
for(int i=1;i<=n;i++)printf("%d ",d[i]);
return 0;
}
负环和差分约束
负环
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+105,M=6e3+105;
int n,m,h[N],nex[M],to[M],cnt,d[N],tot[N],T,x,y,z,val[M];
bool v[N];
inline void add(int x,int y,int z){
to[++cnt]=y;val[cnt]=z;
nex[cnt]=h[x];h[x]=cnt;
}
bool spfa(int s){
memset(v,0,sizeof(v));memset(d,0x3f,sizeof(d));
memset(tot,0,sizeof(tot));
queue<int> q;d[s]=0;v[s]=1;q.push(s);
while(!q.empty()){
int x=q.front();q.pop();v[x]=0;
for(int i=h[x];i;i=nex[i])
if(d[to[i]]>d[x]+val[i]){
d[to[i]]=d[x]+val[i];
if(!v[to[i]]){
if(++tot[to[i]]>n) return 1;
q.push(to[i]);v[to[i]]=1;
}
}
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
int main(){
T=read();
while(T--){
n=read();m=read();
for(int i=1;i<=m;i++){
x=read();y=read();z=read();
add(x,y,z);if(z>=0)add(y,x,z);
}
spfa(1)?puts("YES"):puts("NO");
memset(h,0,sizeof(h));cnt=0;
}
return 0;
}
差分约束
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+105,M=6e4+105;
//变量名
int n,m,h[N],nex[M],to[M],v[M],cnt,d[N],tot[N],T,x,y,z;
bool f[N];
//建图 ,to,cnt,nex,v,h
inline void add(int x,int y,int z){
to[++cnt]=y;v[cnt]=z;
nex[cnt]=h[x];h[x]=cnt;
}
//判负环 ,f,d,tot
bool spfa(int s){
memset(f,0,sizeof(f));memset(d,0x3f,sizeof(d));memset(tot,0,sizeof(tot));
queue<int> q;
d[s]=0;f[s]=1;q.push(s);
while(!q.empty()){
int x=q.front();q.pop();f[x]=0;
for(int i=h[x];i;i=nex[i])
if(d[to[i]]>d[x]+v[i]){
d[to[i]]=d[x]+v[i];
if(!f[to[i]]){
if(++tot[to[i]]>n) return 1;//有负环->无解
q.push(to[i]);f[to[i]]=1;
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) add(0,i,0);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(y,x,z);//这里注意
}
if(spfa(0))printf("NO");
else for(int i=1;i<=n;i++)printf("%d ",d[i]);
return 0;
}
floyd
分层最短路
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0;char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return x;
}
const int N=110005;
int n,m,k;
int head[N],cnt;
int dis[N];
bool vis[N];
struct Node{
int to,nxt,val;
}e[2500001];
void add(int u,int v,int w){
e[++cnt]=(Node){v,head[u],w};
head[u]=cnt;
}
priority_queue<pair<int,int> > q;
void dij(int s){
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[s]=0;
q.push(make_pair(0,s));
while(!q.empty()){
int u=q.top().second;
q.pop();
if(!vis[u]){
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(dis[to]>dis[u]-e[i].val){
dis[to]=dis[u]-e[i].val;
q.push(make_pair(-dis[to],to));
}
}
}
}
}
int main(){
int n=read(),m=read(),k=read(),s=read(),t=read();
for(int i=0,u,v,w;i<m;i++){
u=read(),v=read(),w=read();
add(u,v,-w);
add(v,u,-w);
for(int j=1;j<=k;j++){
add(u+(j-1)*n,v+j*n,0);
add(v+(j-1)*n,u+j*n,0);
add(u+j*n,v+j*n,-w);
add(v+j*n,u+j*n,-w);
}
}
for(int i=1;i<=k;i++){
add(t+(i-1)*n,t+i*n,0);
}
dij(s);
cout<<dis[t+k*n];
return 0;
}
线段树优化建图
最小生成树
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,cnt,f[200105];
struct edge{int fro,to;long long v;}e[200105];
inline bool cmp(edge x,edge y){return x.v<y.v;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline void Kruskal(long long &ans){
for(int i=0;i<n;i++)f[i]=i;sort(e,e+m,cmp);
for(int i=0;i<m;i++){
x=find(e[i].fro);y=find(e[i].to);
if(x!=y){ans+=e[i].v;f[x]=y;cnt++;}
if(cnt==n-1) break;
}
if(cnt<n-1) {printf("orz");exit(0);}
}
int main(){
cin>>n>>m;long long ans=0;
for(int i=0;i<m;i++)scanf("%d%d%d",&e[i].fro,&e[i].to,&e[i].v);
Kruskal(ans);printf("%lld",ans);
return 0;
}
LCA
树的直径
倍增LCA
dfs写法
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+105;
int to[N<<1],nex[N<<1],h[N],cnt,d[N],f[N][27],lg[N],n,m,s;
void add(int x,int y){to[++cnt]=y;nex[cnt]=h[x];h[x]=cnt;}
void pre(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;}
void dfs(int x,int fa){
f[x][0]=fa;d[x]=d[fa]+1;
for(int i=1;i<=19;i++)f[x][i]=f[f[x][i-1]][i-1];
for(int i=h[x];i;i=nex[i])if(to[i]!=fa)dfs(to[i],x);
}
inline int LCA(int x,int y){
if(d[x]<d[y])swap(x,y);
while(d[x]>d[y])x=f[x][lg[d[x]-d[y]]];
if(x==y)return x;
for(int i=lg[d[x]];i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1,x,y;i<n;i++){scanf("%d%d",&x,&y);add(x,y);add(y,x);}
pre();dfs(s,0);
for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y);;printf("%d\n",LCA(x,y));}
return 0;
}
bfs写法
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
树链剖分的LCA
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
const ll maxn = 5e5+5;
int n,m,s;
int head[maxn],nxt[maxn<<1],to[maxn<<1],cnt;
inline void add(int u,int v){nxt[++cnt]=head[u];to[cnt]=v;head[u]=cnt;}
int fa[maxn],son[maxn],dep[maxn],size[maxn];
int top[maxn];
void dfs1(int u,int f){
fa[u]=f;dep[u]=dep[f]+1;size[u]=1;
for(re int i=head[u];i;i=nxt[i]){
if(to[i]==f)continue;
dfs1(to[i],u);size[u]+=size[to[i]];
if(size[to[i]]>size[son[u]])son[u]=to[i];
}
}
void dfs2(int u,int f){
top[u]=f;
if(son[u]==0)return;
dfs2(son[u],f);
for(re int i=head[u];i;i=nxt[i]){
if(to[i]==son[u]||to[i]==fa[u])continue;
dfs2(to[i],to[i]);
}
}
inline int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
const int MA = 1 << 23;
char buf[MA], *p1 = buf, *p2 = buf;
#define gc() \
(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MA, stdin), p1 == p2) \
? EOF \
: *p1++)
inline ll read(){
ll x=0,f=1;char ch=gc();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=x*10+(ch^48);ch=gc();}
return x*f;
}
int main() {
n=read();m=read();s=read();
for(re int i=1,u,v;i<n;i++){
u=read();v=read();
add(u,v);add(v,u);
}
dfs1(s,0); dfs2(s,s);
while(m--){
int u,v;
u=read();v=read();
printf("%d\n",lca(u,v));
}
return 0;
}
树上差分
Tarjan算法
缩点
给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+105,M=5e5+105;
int h[M],fro[M],to[M],nex[M],cnt,val[N],d[N],v[N];
int dfn[N],low[N],num,c[N],tot,f[N],n,m,ans=-1e9;
vector<int>G[N];
stack<int>t;
inline void add(int x,int y){nex[++cnt]=h[x];h[x]=cnt;to[cnt]=y;fro[cnt]=x;}
void tarjan(int x){
dfn[x]=low[x]=++num;t.push(x);c[x]=1;
for(int i=h[x];i;i=nex[i])
if(!dfn[to[i]]){tarjan(to[i]);low[x]=min(low[x],low[to[i]]);}
else if(c[to[i]]){low[x]=min(low[x],dfn[to[i]]);}
if(low[x]==dfn[x]){
tot++;
while(1){
int y=t.top();t.pop();
v[y]=tot;d[tot]+=val[y];c[y]=0;
if(x==y)break;
}
}
}
void dfs(int x){
if(f[x])return ;
int ms=0;f[x]=d[x];
for(int i=0;i<G[x].size();i++){if(!f[G[x][i]])dfs(G[x][i]);ms=max(ms,f[G[x][i]]);}
f[x]+=ms;
}
void work(){
for(int i=1,x,y;i<=cnt;i++)
if(v[fro[i]]!=v[to[i]]){x=v[fro[i]],y=v[to[i]];G[x].push_back(y);}
for(int i=1;i<=tot;i++)if(!f[i]){dfs(i);ans=max(ans,f[i]);}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y);add(x,y);}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
work();
printf("%d",ans);
return 0;
}
割点割边
割点
无向连通图中,如果删除某点后,图变成不连通,则称该点为割点。
若当前点为树根的时候,为割点的条件是:有两颗及以上的子树
若当前节点不是树根的时候,条件是 low[y]>=dfn[x]
,就是在 x
之后遍历的点,能够向上翻,最多到x
,
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+107,M=2e5+107;
int h[N],cnt,to[M],nex[M],n,m,dfn[N],low[N],num,ans;
bool cut[N];
inline void add(int x,int y){to[++cnt]=y;nex[cnt]=h[x];h[x]=cnt;}
void tarjan(int x,int f){
int p=0;dfn[x]=low[x]=++num;
for(int i=h[x];i;i=nex[i]){
if(!dfn[to[i]]){
tarjan(to[i],f);low[x]=min(low[x],low[to[i]]);
if(low[to[i]]>=dfn[x]&&x!=f)cut[x]=1;
if(x==f)p++;
}
low[x]=min(low[x],dfn[to[i]]);
}
if(x==f && p>=2)cut[f]=1;
}
int main(){
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){cin>>u>>v;add(u,v);add(v,u);}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,i);
for(int i=1;i<=n;i++)if(cut[i])ans++;
cout<<ans<<endl;
for(int i=1;i<=n;i++)if(cut[i])cout<<i<<" ";
return 0;
}
割边/桥
点分治
给定一棵有 n 个点的树,询问树上距离为 k 的点对是否存在。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define re register
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int n,m,ask[maxn],d[maxn],t[maxn],r[maxn],q[maxn],h[maxn],to[maxn],nex[maxn],v[maxn],cnt,siz[maxn],mx[maxn],rt,sum;
bool pd[INF],vis[maxn];
inline void add(int x,int y,int val){
nex[++cnt]=h[x];h[x]=cnt;
to[cnt]=y;v[cnt]=val;
}
inline int max(int a,int b){return a>b?a:b;}
void zzx(int u,int f){
siz[u]=1;mx[u]=0;
for(re int i=h[u];i;i=nex[i]){
if(to[i]==f||vis[to[i]])continue;
zzx(to[i],u);
siz[u]+=siz[to[i]];mx[u]=max(mx[u],siz[to[i]]);
}
mx[u]=max(mx[u],sum-siz[u]);
if(mx[u]<mx[rt])rt=u;
}
void zjl(int u,int f){
r[++r[0]]=d[u];
for(re int i=h[u];i;i=nex[i]){
if(to[i]==f||vis[to[i]])continue;
d[to[i]]=d[u]+v[i];
zjl(to[i],u);
}
}
void calc(int u){
int cnt=0;
for(re int i=h[u];i;i=nex[i]){
if(vis[to[i]])continue;
r[0]=0;d[to[i]]=v[i];
zjl(to[i],u);
for(re int j=r[0];j>=1;j--)for(re int k=1;k<=m;k++)if(ask[k]>=r[j])t[k]|=pd[ask[k]-r[j]];
for(re int j=r[0];j>=1;j--){pd[r[j]]=1;q[++cnt]=r[j];}
}
for(re int i=1;i<=cnt;i++)pd[q[i]]=0;
}
void solve(int u){
vis[u]=q;pd[0]=1;calc(u);
for(re int i=h[u];i;i=nex[i]){
if(vis[to[i]])continue;
sum=siz[to[i]];mx[rt=0]=INF;
zzx(to[i],0);solve(rt);
}
}
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}return x*f;}
int main(){
n=read();m=read();
for(re int i=1,u,v,val;i<n;i++){
u=read();v=read();val=read();
add(u,v,val);add(v,u,val);
}
for(re int i=1;i<=m;i++)ask[i]=read();
mx[rt]=INF;sum=n;
zzx(1,0);
solve(rt);
for(re int i=1;i<=m;i++){
if(t[i])printf("AYE\n");
else printf("NAY\n");
}
return 0;
}
给定一棵 n 个节点的树,每条边有边权,求出树上两点距离小于等于 k 的点对数量。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define re register
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int n,m,ask[maxn],d[maxn],t[maxn],r[maxn],q[maxn],h[maxn],to[maxn],nex[maxn],v[maxn],cnt,siz[maxn],mx[maxn],rt,sum,k,ans;
bool pd[INF],vis[maxn],flag;
inline void add(int x,int y,int val){
nex[++cnt]=h[x];h[x]=cnt;
to[cnt]=y;v[cnt]=val;
}
inline int max(int a,int b){return a>b?a:b;}
void zzx(int u,int f){
siz[u]=1;mx[u]=0;
for(re int i=h[u];i;i=nex[i]){
if(to[i]==f||vis[to[i]])continue;
zzx(to[i],u);
siz[u]+=siz[to[i]];mx[u]=max(mx[u],siz[to[i]]);
}
mx[u]=max(mx[u],sum-siz[u]);
if(mx[u]<mx[rt])rt=u;
}
void zjl(int u,int f){
r[++r[0]]=d[u];
for(re int i=h[u];i;i=nex[i]){
if(to[i]==f||vis[to[i]])continue;
d[to[i]]=d[u]+v[i];
zjl(to[i],u);
}
}
int calc(int u,int val){
r[0]=0;d[u]=val;zjl(u,0);
sort(r+1,r+1+r[0]);
int le=1,ri=r[0],res=0;
while(le<=ri){if(r[le]+r[ri]<=k) res+=ri-le,le++;else ri--;}
return res;
}
void solve(int u){
vis[u]=1;ans+=calc(u,0);
for(re int i=h[u];i;i=nex[i]){
if(vis[to[i]])continue;
ans-=calc(to[i],v[i]);sum=siz[to[i]];mx[rt=0]=n;
zzx(to[i],u);solve(rt);
}
}
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}return x*f;}
int main(){
n=read();
for(re int i=1,u,v,val;i<n;i++){
u=read();v=read();val=read();
add(u,v,val);add(v,u,val);
}
k=read();
mx[rt]=sum=n;
zzx(1,0);
solve(rt);
printf("%d",ans);
return 0;
}
二分图匹配
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+105;
int h[N],to[N],nex[N],cnt,n,m,e,ans,match[N];
bool v[N];
inline void add(int x,int y){to[++cnt]=y;nex[cnt]=h[x];h[x]=cnt;}
bool dfs(int x){
for(int i=h[x];i;i=nex[i])
if(!v[to[i]]){
v[to[i]]=1;
if(!match[to[i]]||dfs(match[to[i]])) {match[to[i]]=x;return 1;}
}
return 0;
}
int main(){
cin>>n>>m>>e;
for(int i=1,x,y;i<=e;i++){
cin>>x>>y;
add(x,y+n);add(y+n,x);
}
for(int i=1;i<=n;i++){
memset(v,0,sizeof(v));
if(dfs(i)) ans++;
}
cout<<ans;
return 0;
}
网络流初步
最大流
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
const int maxn = 10001, maxm = 200005;
int n = 0, m = 0, s = 0, t = 0;
int head[maxn], nxt[maxm], to[maxm], val[maxm], cnt = 1;
inline void insert(int u, int e, int v) { nxt[++cnt] = head[u]; head[u] = cnt; to[cnt] = e; val[cnt] = v; }
bool vis[maxn];
int d[maxn];
bool bfs() {
memset(vis, 0, sizeof(vis));
std::queue<int> q;
q.push(s); vis[s] = true;
d[s] = 1;
while (!q.empty()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = nxt[i])
if (!vis[to[i]] && val[i]) {
d[to[i]] = d[x] + 1;
if (to[i] == t) return true;
vis[to[i]] = true;
q.push(to[i]);
}
}
return false;
}
inline int min(int a, int b) { return a < b ? a : b; }
int dinic(int x, int flow) {
if (x == t) return flow;
int use = 0, k = 0;
for (int i = head[x]; i; i = nxt[i]) {
if (val[i] && d[to[i]] == d[x] + 1) {
k = dinic(to[i], min(flow - use, val[i]));
if (!k) d[to[i]] = 0;
val[i] -= k;
val[i ^ 1] += k;
use += k;
}
}
return use;
}
signed main() {
scanf("%lld%lld%lld%lld", &n, &m, &s, &t);
int a = 0, b = 0, c = 0;
for (int i = 1; i <= m; ++i) {
scanf("%lld%lld%lld", &a, &b, &c);
insert(a, b, c); insert(b, a, 0);
}
int ans = 0;
while (bfs())
ans += dinic(s, 0x7fffffff);
printf("%lld", ans);
return 0;
}
最小费用最大流
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//#define LawrenceSivan
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define INF 0x3f3f3f3f3f3f3f3f
#define re register
#define int ll
const int maxn=5e3+5;
const int maxm=5e4+5;
int n,m,s,t;
int head[maxn],to[maxm<<1],nxt[maxm<<1],flow[maxm<<1],cost[maxm<<1],cnt=1;
int maxflow,mincost;
inline void add(int u,int v,int _flow,int _cost){
nxt[++cnt]=head[u];to[cnt]=v;flow[cnt]=_flow;
cost[cnt]=_cost;head[u]=cnt;
nxt[++cnt]=head[v];to[cnt]=u;flow[cnt]=0;
cost[cnt]=-_cost;head[v]=cnt;
}
int incf[maxn];
int dis[maxn],pre[maxn];
bool vis[maxn];
queue <int> q;
inline bool SPFA(){
memset(dis,0x3f,sizeof(dis));
memset(vis,false,sizeof(vis));
q.push(s),dis[s]=0;vis[s]=true;incf[s]=INF;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=false;
for(re int i=head[u];i;i=nxt[i]){
int v=to[i];
if(!flow[i])continue;
if(dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
incf[v]=min(incf[u],flow[i]);
pre[v]=i;
if(!vis[v])q.push(v),vis[v]=true;
}
}
}
if(dis[t]==INF)return false;
return true;
}
inline void MCMF(){
while(SPFA()){
int x=t;
maxflow+=incf[t];
mincost+=incf[t]*dis[t];
while(x!=s){
int i=pre[x];
flow[i]-=incf[t];
flow[i^1]+=incf[t];
x=to[i^1];
}
}
}
namespace IO{
template<typename T>
inline void read(T &x){
x=0;T f=1;char ch=getchar();
while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x*=f;
}
template <typename T, typename... Args>
inline void read(T& t, Args&... args) {
read(t); read(args...);
}
template<typename T>
void write(T x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
template<typename T,typename... Args>
void write(T t,Args... args){
write(t);putchar(' ');write(args...);
}
}
using namespace IO;
signed main() {
#ifdef LawrenceSivan
freopen("aa.in","r", stdin);
freopen("aa.out","w", stdout);
#endif
read(n,m,s,t);
for(re int i=1,u,v,f,c;i<=m;i++){
read(u,v,f,c);
add(u,v,f,c);
}
MCMF();
write(maxflow,mincost),puts("");
return 0;
}
数论
素数相关
线性筛素数
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
bool v[100000005];
int n,q,k,cnt;
int p[10000005];
void prim(int n){
for(int i=2;i<=n;i++){
v[1]=1;
if(v[i]==0) p[cnt++]=i;
for(int j=0;j<cnt&&p[j]*i<=n;j++){v[i*p[j]]=1;if(i%p[j]==0) break;}
}
}
int main(){
std::ios::sync_with_stdio(0);
cin>>n>>q;
prim(n);//质数的英文怎么拼写来着
while(q--){
cin>>k;
cout<<p[k-1]<<endl;
}
return 0;
}
欧几里得
裴蜀定理
给定一个包含 nnn 个元素的整数序列 AAA,记作 A1,A2,A3,...,AnA_1,A_2,A_3,...,A_nA1,A2,A3,...,An。
求另一个包含 nnn 个元素的待定整数序列 XXX,记 S=∑i=1nAi×XiS=\sum\limits_{i=1}^nA_i\times X_iS=i=1∑nAi×Xi,使得 S>0S>0S>0 且 SSS 尽可能的小。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
int n,s,x,y;
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int main(){
cin>>n;
if(n==1){
cin>>x;
if(x<0)x=-x;
cout<<x;
}
if(n>=2){
cin>>x>>y;
if(x<0)x=-x;
if(y<0)y=-y;
s=gcd(x,y);
}
n-=2;
while(n--){
cin>>x;
if(x<0)x=-x;
s=gcd(s,x);
}
cout<<s;
return 0;
}
欧几里得算法(gcd)
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
扩展欧几里得算法
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//同余方程:ax+by=c
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,c,x,y;
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
int main(){
cin>>a>>b;c=1;
ll GCD=gcd(a,b);
if(c%GCD!=0) cout<<"Orz,I cannot find x!";
else{
exgcd(a,b,x,y);
x=(x+b)%b;
ll ans=((c/GCD)*x+(b/GCD))%(b/GCD);
cout<<ans<<endl;
}
return 0;
}
欧拉定理
欧拉定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
扩展欧拉定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//扩展欧拉定理,求a^b mod m
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,m,b;
int phi(int x){
int ans=1,num=1;
for(int i=2;i*i<=x;i++)
if(!(x%i)){
num=i-1,x/=i;
while(!(x%i)) num=num*i,x/=i;
ans=num*ans;
}
if(x!=1) ans=ans*(x-1);
return ans;
}
inline ll read(ll m){
ll x=0,f=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){
x=x*10+ch-'0';
if(x>=m) f=1;
x%=m;ch=getchar();
}
return x+(f==1?m:0);
}
inline ll qp(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a*a%p;b>>=1;
}
return ans%p;
}
int main(){
cin>>a>>m;b=read(phi(m));
cout<<qp(a,b,m);
return 0;
}
无穷幂
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+105,M=1e6+105;
int n,cnt,p[M],phi[N],T,P;
bool v[N];
void xxs(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!v[i])p[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&i*p[j]<=n;j++){
v[i*p[j]]=1;
if(i%p[j]==0){phi[i*p[j]]=phi[i]*p[j];break;}
else phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
}
int ksm(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;b=b>>1;
}
return res%p;
}
int work(int p){
if(p==1) return 0;
return ksm(2,work(phi[p])+phi[p],p);
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
signed main(){
xxs(N-105);T=read();
while(T--){P=read();printf("%lld\n",work(P));}
return 0;
}
中国剩余定理(CRT)
中国剩余定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//中国剩余定理
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=21;
ll m[N],a[N],M=1,n,ans;
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){cin>>m[i];M*=m[i];cin>>a[i];}
for(int i=1;i<=n;i++){
ll mi=M/m[i],x=0,y=0;
exgcd(mi,m[i],x,y);
ans+=a[i]*mi*(x<0?x+m[i]:x);
}
cout<<(ans+M)%M;
return 0;
}
扩展中国剩余定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//扩展中国剩余定理(EXCRT)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[100105],b[100105],ans,m,x,y,n;
ll EXGCD(ll a,ll b,ll &x,ll &y){
ll ans;
if(b==0){x=1,y=0;return a;}
ans=EXGCD(b,a%b,y,x);y-=a/b*x;
return ans;
}
ll mymul(ll n,ll k,ll mod){
ll ans=0;
while(k){
if(k&1) ans=(ans+n)%mod;
n=(n+n)%mod;k>>=1;
}
return ans;
}//龟速乘
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>b[i]>>a[i];
ans=a[1],m=b[1];
for(int i=2;i<=n;i++){
ll B=((a[i]-ans)%b[i]+b[i])%b[i];
ll GCD=EXGCD(m,b[i],x,y);
x=mymul(x,B/GCD,b[i]);
ans+=m*x;
m*=b[i]/GCD;
ans=(ans+m)%m;
}
cout<<ans;
return 0;
}
扩展扩展中国剩余定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+105;
int t,n,m,a[N],p[N],j[N],x,y,G,b[N],mx,c;
multiset<int> g;
inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
void exgcd(int a,int b,int &x,int &y){
if(b==0){x=1,y=0;G=a;return ;}
exgcd(b,a%b,y,x);y-=a/b*x;
}
inline int mul(int a,int b,int p){
int ans=0;
while(b){
if(b&1)ans=(ans+a)%p;
a=(a+a)%p;b>>=1;
}
return ans;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
signed main(){
t=read();
outdoor:
while(t--){
n=read(),m=read();mx=c=0;
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)p[i]=read();
for(int i=1;i<=n;i++)j[i]=read();
for(int i=1;i<=m;i++){int x=read();g.insert(x);}
m=1;
for(int i=1;i<=n;i++){
multiset<int>::iterator pos;
pos=g.upper_bound(a[i]); if(pos!=g.begin())pos--;
b[i]=*pos;
g.erase(pos);g.insert(j[i]);
mx=max(mx,(a[i]-1)/b[i]+1);
b[i]%=p[i];a[i]%=p[i];
if(!b[i] && a[i]) {cout<<"-1";goto outdoor;}
if(!b[i] && !a[i])continue;
exgcd(b[i],p[i],x,y);
if(a[i]%G){cout<<"-1";goto outdoor;}
p[i]/=G;a[i]=mul(a[i]/G,(x%p[i]+p[i])%p[i],p[i]);
exgcd(m,p[i],x,y);
if((a[i]-c)%G){cout<<"-1";goto outdoor;}
m=m/G*p[i];
c=(c+mul(mul(m/p[i],((a[i]-c)%m+m)%m,m),(x%m+m)%m,m))%m;
}
int ANS=c>mx?c:c+m*(mx-c-1)/m+1;
cout<<ANS<<endl;
g.clear();
}
return 0;
}
卢卡斯
卢卡斯定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//卢卡斯定理
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100010;
ll a[maxn],p;
ll mypow(ll y,ll z,int p){
ll ans=1;
while(z){
if(z&1)ans=ans*y%p;
y=y*y%p;z>>=1;
}
return ans%p;
}
ll c(ll n,ll m){
if(m>n)return 0;
return ((a[n]*mypow(a[m],p-2,p))%p*mypow(a[n-m],p-2,p)%p);
}
ll lucas(ll n,ll m){
if(!m)return 1;
return c(n%p,m%p)*lucas(n/p,m/p)%p;
}
inline ll read(){
ll x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
int main(){
int t=read();
while(t--){
int n=read();int m=read();p=read();
a[0]=1;
for(int i=1;i<=p;i++){a[i]=(a[i-1]*i)%p;}
cout<<lucas(n+m,n)<<endl;
}
return 0;
}
扩展卢卡斯定理
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll A[1001],B[1001];
void exgcd(ll a,ll b,ll &x, ll &y){
if(b==0){x=1,y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
inline ll inv(ll a,ll p){
ll x,y;exgcd(a,p,x,y);
return (x+p)%p;
}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
inline ll myabs(ll x){return x?x:-x;}
inline ll fmul(ll a,ll b,ll p){
ll t=0;a%=p;b%=p;
while(b){
if(b&1ll)t=t+a%p;
b>>=1ll;a=a+a%p;
}
return t;
}
inline ll fpow(ll a,ll b,ll p){
ll res=1;
while(b){
if(b&1ll)res=res*a%p;
a=a*a%p;b>>=1ll;
}
return res%p;
}
inline ll read(){
ll x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll F(ll n,ll p,ll pk){
if(n==0)return 1;
ll rou=1;ll rem=1;
for(ll i=1;i<=pk;i++){if(i%p)rou=rou*i%pk;}
rou=fpow(rou,n/pk,pk);
for(ll i=pk*(n/pk);i<=n;i++){if(i%p)rem=rem*(i%pk)%pk;}
return F(n/p,p,pk)*rou%pk*rem%pk;
}
inline ll G(ll n,ll p){
if(n<p)return 0;
return G(n/p,p)+(n/p);
}
inline ll c_pk(ll n,ll m,ll p,ll pk){
ll fz=F(n,p,pk),fm1=inv(F(m,p,pk),pk),fm2=inv(F(n-m,p,pk),pk);
ll mi=fpow(p,G(n,p)-G(m,p)-G(n-m,p),pk);
return fz*fm1%pk*fm2%pk*mi%pk;
}
inline ll exlucas(ll n,ll m,ll p){
ll lcj=p,tot=0;
for(ll tmp=2;tmp*tmp<=p;tmp++)
if(!(lcj%tmp)){
ll pk=1;
while(!(lcj%tmp)){pk*=tmp;lcj/=tmp;}
A[++tot]=pk,B[tot]=c_pk(n,m,tmp,pk);
}
if(lcj!=1){A[++tot]=lcj;B[tot]=c_pk(n,m,lcj,lcj);}
ll ans=0;
for(ll i=1;i<=tot;i++){
ll M=p/A[i],T=inv(M,A[i]);
ans=(ans+B[i]*M%p*T%p)%p;
}
return ans;
}
int main(){
ll n=read();ll m=read();ll p=read();
printf("%lld\n",exlucas(n,m,p));
return 0;
}
BSGS
给定一个质数 ppp,以及一个整数 bbb,一个整数 nnn,现在要求你计算一个最小的非负整数 lll,满足 bl≡n(modp)b^l \equiv n \pmod pbl≡n(modp)
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,p;
int ksm(int a,int b,int p){
int ans=1;
while(b>0){
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int BSGS(int a,int b,int p){
map<int,int> hash;hash.clear();
b%=p;
int t=(int)sqrt(p)+1;
for(int j=0;j<t;j++){
int val=(long long)b*ksm(a,j,p)%p;
hash[val]=j;
}
a=ksm(a,t,p);
if(a==0) return b==0?1:-1;
for(int i=0;i<=t;i++){
int val=ksm(a,i,p);
int j=hash.find(val)==hash.end()?-1:hash[val];
if(j>=0 && i*t-j>=0) return i*t-j;
}
return -1;
}
signed main(){
cin>>p>>a>>b;
int ans=BSGS(a,b,p);
if(ans==-1) cout<<"no solution";
else cout<<ans;
}
扩展BSGS
给定 a,p,ba,p,ba,p,b,求满足 ax≡b(modp)a^x≡b \pmod pax≡b(modp) 的最小自然数 xxx
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,p;
map<int,int>hash;
inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
inline void exgcd(int a,int b,int &x,int &y){
if(b==0)x=1,y=0;
else{exgcd(b,a%b,y,x);y-=a/b*x;}
}
inline int inv(int a,int b){
int x,y;
exgcd(a,b,x,y);
return (x%b+b)%b;
}
inline int ksm(int a,int b,int p){
int ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
inline int BSGS(int a,int b,int p){
hash.clear();
int k=ceil(sqrt(p));
b%=p;
for(int i=1;i<=k;i++){b=b*a%p,hash[b]=i;}
int tmp=ksm(a,k,p);b=1;
for(int i=1;i<=k;i++){
b=b*tmp%p;
if(hash.find(b)!=hash.end()) return (i*k-hash[b]+p)%p;
}
return -1;
}
inline int EXBSGS(int a,int b,int p){
if(b==1||p==1) return 0;
int GCD=gcd(a,p),k=0,q=1;
while(GCD>1){
if(b%GCD!=0) return -1;
k++;b/=GCD,p/=GCD;q=q*(a/GCD)%p;
if(q==b) return k;
GCD=gcd(a,p);
}
int flag=BSGS(a,b*inv(q,p)%p,p);
if(flag==-1) return -1;
return flag+k;
}
namespace Read{
template<typename T>
inline void read(T &x){
x=0;T f=1;char ch=getchar();
while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x*=f;
}
template <typename T, typename... Args>
inline void read(T& t, Args&... args) {
read(t); read(args...);
}
}
using namespace Read;
signed main(){
read(a,p,b);
while(a || b || p){
a%=p,b%=p;
int ans=EXBSGS(a,b,p);
if(ans==-1) puts("No Solution");
else printf("%d\n",ans);
read(a,p,b);
}
return 0;
}
乘法逆元
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e6+5;
ll inv[maxn];
int main(){
int n,p;
scanf("%d%d",&n,&p);
cout<<"1"<<endl;
inv[1]=1;
for(int i=2;i<=n;i++){
inv[i]=(ll)p-(p/i)*inv[p%i]%p;
printf("%d\n",inv[i]);
}
return 0;
}
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e6;
#define re register
inline int read(){
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n,k,mod,inv[maxn],a[maxn],s[maxn];
inline int fpow(int a,int b,int p){
int ans=1;
while(b){
if(b&1)ans=(ll)ans*a%p;
a=(ll)a*a%p;
b>>=1;
}
return ans%p;
}
int main(){
n=read();
mod=read();
k=read();
int ans=0;
memset(s,1,sizeof s);
for(re int i=1;i<=n;++i){
a[i]=read();
s[i]=(ll)s[i-1]*a[i]%mod;
}
inv[n]=fpow(s[n],mod-2,mod);
for(re int i=n-1;i>0;--i) inv[i]=(ll)inv[i+1]*a[i+1]%mod;
for(re int i=n;i>0;--i) ans=((ll)inv[i]*s[i-1]%mod+ans)*k %mod;
printf("%d\n",ans);
return 0;
}
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
矩阵
矩阵乘法
矩阵快速幂
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=205,mod=1e9+7;
int n,k,a[N][N],b[N][N],c[N][N];
inline void c1(){//自乘
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)b[i][j]=(b[i][j]+a[i][k]*a[k][j])%mod;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=b[i][j];
}
inline void c2(){//答案乘
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)b[i][j]=(b[i][j]+c[i][k]*a[k][j])%mod;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)c[i][j]=b[i][j];
}
void ksm(int k){
for(int i=1;i<=n;i++)c[i][i]=1;//单位矩阵,等价于:int ans=1;
while(k){if(k&1){memset(b,0,sizeof(b));c2();}k>>=1;memset(b,0,sizeof(b));c1();}
}
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}return x*f;}
signed main(){
n=read();k=read();
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=read();
ksm(k);
for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%lld ",c[i][j]);printf("\n");}
return 0;
}
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll n,k;
struct matrix{
ll A[115][115];
inline void init(){memset(A,0,sizeof(A));}
inline ll* operator [] (const int k){return A[k];}
inline matrix operator *(matrix &B){
matrix res;res.init();
for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int t=0;t<n;t++)
res[i][j]=(res[i][j]+A[i][t]*B[t][j])%mod;
return res;
}
inline matrix ksm(ll x){
matrix res;res.init();matrix tmp=*this;
for(int i=0;i<n;i++) res[i][i]=1;
while(x){
if(x&1) res=res*tmp;
tmp=tmp*tmp;x>>=1;
}
return res;
}
}MAP;
int main(){
scanf("%lld%lld",&n,&k);
for(int i=0;i<n;i++)for(int j=0;j<n;j++)scanf("%lld",&MAP[i][j]);
MAP=MAP.ksm(k);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) printf("%lld ",MAP[i][j]);
putchar('\n');
}
return 0;
}
矩阵加速
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=205;
const int mod=1e9+7;
int n,k,a[N][N],b[N][N],c[N][N];
inline void c1(){
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
b[i][j]=(b[i][j]+a[i][k]*a[k][j])%mod;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=b[i][j];
}
inline void c2(){
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
b[i][j]=(b[i][j]+c[i][k]*a[k][j])%mod;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
c[i][j]=b[i][j];
}
void ksm(int b){
for(int i=1;i<=n;i++)c[i][i]=1;
while(b){
if(b&1)c2();
b>>=1;c1();
}
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
signed main(){
k=read();n=2;
if(k==1 || k==2){printf("1");return 0;}
a[1][1]=1;a[1][2]=1;a[2][1]=1;a[2][2]=0;
int f[1][2];f[1][1]=1;f[1][2]=1;
ksm(k-2);
memset(b,0,sizeof(b));
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
b[1][j]=(b[1][j]+f[1][k]*c[k][j])%mod;
printf("%lld",b[1][1]);
return 0;
}
高斯消元
给定一个线性方程组,对其求解
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
int n = 0;
double k[101][101], b[101];
bool Gauss()
{
for (int i = 1; i <= n; ++i)
{
int ex = i;
for (int j = i + 1; j <= n; ++j)
if (fabs(k[j][i]) > fabs(k[ex][i]))
ex = j;
if (fabs(k[ex][i]) < 1e-8)
return false;
if (ex != i) {
for (int p = 1; p <= n; ++p)
swap(k[i][p], k[ex][p]);
swap(b[i], b[ex]);
}
for (int j = 1; j <= n; ++j)
{
if (j == i || fabs(k[j][i]) < 1e-8)
continue;
double c = k[j][i] / k[i][i];
for (int p = 1; p <= n; ++p)
k[j][p] -= k[i][p] * c;
b[j] -= b[i] * c;
}
}
return true;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
scanf("%lf", &k[i][j]);
scanf("%lf", &b[i]);
}
if(Gauss())
for (int i = 1; i <= n;++i)
printf("%.2lf\n", b[i] / k[i][i]);
else
printf("No Solution");
return 0;
}
组合数
数据结构
并查集
路径压缩
扩展域和边带权
ST表
正写ST表
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
int n,m,s[100005][25];
int query(int l,int r){
int k=log2(r-l+1);
return max(s[l][k],s[r-(1<<k)+1][k]);
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
void work(int n){
for(int i=1;i<=n;i++)s[i][0]=read();
for(int j=1;j<=19;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
s[i][j]=max(s[i][j-1],s[i+(1<<(j-1))][j-1]);
}
int main(){
n=read();m=read();
work(n);
while(m--){
int l=read(),r=read();
printf("%d\n",query(l,r));
}
return 0;
}
反写ST表
树状数组
基本操作
二分与倍增
二维树状数组
分块
分块初步
求众数
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int n,m,block;
int a[N],b[N],f[N],tot;
int d[1000][1000];
vector<int> g[N];
int ct[N];
inline void build(){
block=max(1,(int)(n/sqrt(m*log2(n))));
for(int i=1;i<=n;i++)b[i]=(i-1)/block+1;
}
inline int ef(int l,int r,int val){
int t=upper_bound(g[val].begin(),g[val].end(),r)-lower_bound(g[val].begin(),g[val].end(),l);
return t;
}
inline void pre(int x){
memset(ct,0,sizeof(ct));
int mx=-1,ans=0;
for (int i =(x-1)*block+1;i<=n;i++){
ct[a[i]]++;
if (ct[a[i]]>mx||(ct[a[i]]==mx&&a[i]<ans)){ans=a[i];mx=ct[a[i]];}
d[x][b[i]]=ans;
}
}
inline int query(int l,int r){
int ans=d[b[l]+1][b[r]-1];
int mx=ef(l,r,ans);
int cnt=0;
int up=min(r,b[l]*block);
for(int i=l;i<=up;i++){
cnt=ef(l, r, a[i]);
if(cnt>mx||(cnt==mx&&f[ans]>f[a[i]])){mx=cnt;ans=a[i];}
}
if(b[l]!=b[r]){
for(int i=(b[r]-1)*block+1;i<=r;i++){
cnt=ef(l,r,a[i]);
if(cnt>mx||(cnt==mx&&ans>a[i])){mx=cnt;ans=a[i];}
}
}
return ans;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
int main(){
n=read();m=read();
for (int i=1;i<=n;i++){a[i]=read();f[i]=a[i];}
build();
sort(f+1,f+n+1);
int N=unique(f+1,f+n+1)-f-1;
for (int i=1;i<=n;i++){
a[i]=lower_bound(f+1,f+N+1,a[i])-f;
g[a[i]].push_back(i);
}
int ans=0;
for(int i=1;i<=b[n];i++)pre(i);
while (m--){
int l,r;
l=read();r=read();
l=(l+ans-1)%n+1;r=(r+ans-1)%n+1;
if(l>r)swap(l,r);
ans=f[query(l,r)];
printf("%d\n",ans);
}
return 0;
}
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
// 1 x y k:将区间 [x,y] 内每个数加上 k。
// 2 x y:输出区间 [x,y] 内每个数的和。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+105;
int n,m,block,ed[N],st[N],pos[N],t;
int a[N],add[N],sum[N];
void pre(){
block=sqrt(n);
t=n/block;
if(n%block) t++;
for(int i=1;i<=t;i++){
st[i]=(i-1)*block+1;
ed[i]=i*block;
}
ed[t]=n;
for(int i=1;i<=n;i++)pos[i]=(i-1)/block+1;
for(int i=1;i<=t;i++)for(int j=st[i];j<=ed[i];j++) sum[i]+=a[j];
}
void change(int L,int R,int d){
int p=pos[L],q=pos[R];
if(p==q){
for(int i=L;i<=R;i++) a[i]+=d;
sum[p]+=d*(R-L+1);
}
else{
for(int i=p+1;i<=q-1;i++) add[i]+=d;
for(int i=L;i<=ed[p];i++) a[i]+=d;
sum[p]+=d*(ed[p]-L+1);
for(int i=st[q];i<=R;i++) a[i]+=d;
sum[q]+=d*(R-st[q]+1);
}
}
long long ask(int L,int R){
int p=pos[L],q=pos[R];
long long ans=0;
if(p==q){
for(int i=L;i<=R;i++) ans+=a[i];
ans+=add[p]*(R-L+1);
}
else{
for(int i=p+1;i<=q-1;i++) ans+=sum[i]+add[i]*(ed[i]-st[i]+1);
for(int i=L;i<=ed[p];i++) ans+=a[i];
ans+=add[p]*(ed[p]-L+1);
for(int i=st[q];i<=R;i++) ans+=a[i];
ans+=add[q]*(R-st[q]+1);
}
return ans;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;i++)a[i]=read();
pre();
for(int i=1;i<=m;i++){
int opt,x,y,z;
opt=read();
if(opt==1){
x=read();y=read();z=read();
change(x,y,z);
}
if(opt==2){
x=read();y=read();
long long ans=ask(x,y);
printf("%lld\n",ans);
}
}
return 0;
}
线段树
线段树初步
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+5;
unsigned ll n,m,a[maxn],ans[maxn<<2],tag[maxn<<2];
inline ll ls(int p){return p<<1;}
inline ll rs(ll p){return p<<1|1;}
void scan(){
cin>>n>>m;
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
}
inline void push_up(ll p){
ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r){
tag[p]=0;
if(l==r){
ans[p]=a[l];
return ;
}
ll mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
inline void f(ll p,ll l,ll r,ll k){
tag[p]=tag[p]+k;
ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r){
ll mid=(l+r)>>1;
f(ls(p),l,mid,tag[p]);
f(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
inline void update(ll n1,ll nr,ll l,ll r,ll p,ll k){
if(n1<=l&&r<=nr){
ans[p]+=k*(r-l+1);
tag[p]+=k;
return ;
}
push_down(p,l,r);
ll mid=(l+r)>>1;
if(n1<=mid)update(n1,nr,l,mid,ls(p),k);
if(nr>mid)update(n1,nr,mid+1,r,rs(p),k);
push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p){
ll res=0;
if(q_x<=l&&r<=q_y){
return ans[p];
}
ll mid=(l+r)>>1;
push_down(p,l,r);
if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
if(q_y>mid)res+=query(q_x,q_y,mid+1,r,rs(p));
return res;
}
int main(){
ll a1,b,c,d,e,f;
scan();
build(1,1,n);
while(m--){
scanf("%lld",&a1);
switch(a1){
case 1:{
scanf("%lld%lld%lld",&b,&c,&d);
update(b,c,1,n,1,d);
break;
}
case 2:{
scanf("%lld%lld",&e,&f);
printf("%lld\n",query(e,f,1,n,1));
break;
}
}
}
return 0;
}
平衡树
Treap
FHQ Treap
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//重构次数:2
//题目:FHQ Treap
//不压行
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+105;
int son[N][3],val[N],ran[N],siz[N],SIZE,n,root,x,y,z,opt,a;
void Up(int x){siz[x]=1+siz[son[x][0]]+siz[son[x][1]];}
int New(int v){
siz[++SIZE]=1;
val[SIZE]=v;ran[SIZE]=rand();
return SIZE;
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(ran[x]<ran[y]){
son[x][1]=merge(son[x][1],y);
Up(x);return x;
}
else {
son[y][0]=merge(x,son[y][0]);
Up(y);return y;
}
}
void split(int p,int k,int &x,int &y){
if(!p) x=y=0;
else{
if(val[p]<=k) x=p,split(son[p][1],k,son[p][1],y);
else y=p,split(son[p][0],k,x,son[p][0]);
Up(p);
}
}
int kth(int p,int k){
while(1){
if(k<=siz[son[p][0]])p=son[p][0];
else if(k==siz[son[p][0]]+1) return p;
else k-=siz[son[p][0]]+1,p=son[p][1];
}
}
int main(){
srand(1e9+7);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&opt,&a);
if(opt==1){//插入
split(root,a,x,y);
root=merge(merge(x,New(a)),y);
}
else if(opt==2){
split(root,a,x,z);split(x,a-1,x,y);
y=merge(son[y][0],son[y][1]);root=merge(merge(x,y),z);
}
else if(opt==3){
split(root,a-1,x,y);
printf("%d\n",siz[x]+1);
root=merge(x,y);
}
else if(opt==4){printf("%d\n",val[kth(root,a)]);}
else if(opt==5){
split(root,a-1,x,y);
printf("%d\n",val[kth(x,siz[x])]);
root=merge(x,y);
}
else if(opt==6){
split(root,a,x,y);
printf("%d\n",val[kth(y,1)]);
root=merge(x,y);
}
}
return 0;
}
文艺平衡树
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//重构次数:1
//不压行
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+105;
int son[N][3],val[N],ran[N],siz[N],SIZE,n,root,m;
bool f[N];
void Up(int x){siz[x]=1+siz[son[x][0]]+siz[son[x][1]];}
void Down(int x){
swap(son[x][0],son[x][1]);
if(son[x][0])f[son[x][0]]^=1;
if(son[x][1])f[son[x][1]]^=1;
f[x]=0;
}
int New(int v){
siz[++SIZE]=1;
val[SIZE]=v;ran[SIZE]=rand();
return SIZE;
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(ran[x]<ran[y]){
if(f[x])Down(x);
son[x][1]=merge(son[x][1],y);
Up(x);return x;
}
else {
if(f[y])Down(y);
son[y][0]=merge(x,son[y][0]);
Up(y);return y;
}
}
void split(int p,int k,int &x,int &y){
if(!p) x=y=0;
else{
if(f[p])Down(p);//下面用siz,而不是val
if(siz[son[p][0]]<k) x=p,split(son[p][1],k-siz[son[p][0]]-1,son[p][1],y);
else y=p,split(son[p][0],k,x,son[p][0]);
Up(p);
}
}
/*int kth(int p,int k){
while(1){
if(k<=siz[son[p][0]])p=son[p][0];
else if(k==siz[son[p][0]]+1) return p;
else k-=siz[son[p][0]]+1,p=son[p][1];
}
}*/
void out(int x){
if(!x) return ;
if(f[x])Down(x);
out(son[x][0]);
printf("%d ",val[x]) ;
out(son[x][1]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)root=merge(root,New(i));
for(int i=1;i<=m;i++){
int l,r,x,y,z;
scanf("%d%d",&l,&r);
split(root,l-1,x,y);split(y,r-l+1,y,z);
f[y]^=1;root=merge(x,merge(y,z));
}
out(root);
return 0;
}
主席树
初步
如题,你需要维护这样的一个长度为 N N N 的数组,支持如下几种操作
在某个历史版本上修改某一个位置上的值
访问某个历史版本上的某一位置的值
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
inline int read(){
int f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int n,m,top=0,a[N],root[N<<5];
struct hjttree{
int l,r,val;
}tree[N<<5];
inline int clone(int node){
top++;
tree[top]=tree[node];
return top;
}//н¨½Úµã
inline int build(int node,int l,int r){
node=++top;
if(l==r){
tree[node].val=a[l];
return top;
}
int mid=(l+r)>>1;
tree[node].l=build(tree[node].l,l,mid);
tree[node].r=build(tree[node].r,mid+1,r);
return node;
}
int update(int node,int l,int r,int x,int val){
node=clone(node);//¸üоÍҪн¨½Úµã
if(l==r){
tree[node].val=val;
}
else {
int mid=(l+r)>>1;
if(x<=mid){
tree[node].l=update(tree[node].l,l,mid,x,val);
}
else {
tree[node].r=update(tree[node].r,mid+1,r,x,val);
}
}
return node;
}
int query(int node,int l,int r,int x){
if(l==r){
return tree[node].val;
}
else {
int mid=(l+r)>>1;
if(x<=mid){
return query(tree[node].l,l,mid,x);
}
else {
return query(tree[node].r,mid+1,r,x);
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)cin>>a[i];
root[0]=build(0,1,n);
for(int i=1,opt,mode,x,y;i<=m;i++){
opt=read(),mode=read(),x=read();
if(mode==1){
y=read();
root[i]=update(root[opt],1,n,x,y);
}
else {
cout<<query(root[opt],1,n,x)<<endl;
root[i]=root[opt];
}
}
return 0;
}
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
//主席树
//给定n个数组成的序列a,将对于指定闭区间[l,r]查询其区间内的第k小值
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int lc[N<<5],rc[N<<5],rt[N],sum[N<<5],a[N],b[N],cnt,n,m,p,ql,qr,l,r,k,q,ans;
inline void build(int &x,int l,int r){
int oo=++cnt;
if(l==r)return ;
build(lc[oo],l,((l+r)>>1));build(rc[oo],((l+r)>>1)+1,r);
}
int modify(int o,int l,int r){
int oo=++cnt;
lc[oo]=lc[o];rc[oo]=rc[o];sum[oo]=sum[o]+1;
if(l==r){return oo;}
if(p<=((l+r)>>1))lc[oo]=modify(lc[oo],l,((l+r)>>1));
else rc[oo]=modify(rc[oo],((l+r)>>1)+1,r);
return oo;
}
int query(int u,int v,int l,int r,int k){
int ans,mid=(l+r)>>1,x=sum[lc[v]]-sum[lc[u]];
if(l==r)return l;
if(x>=k)ans=query(lc[u],lc[v],l,mid,k);
else ans=query(rc[u],rc[v],mid+1,r,k-x);
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){cin>>a[i];b[i]=a[i];}
sort(b+1,b+1+n);q=unique(b+1,b+n+1)-b-1;build(rt[0],1,q);
for(int i=1;i<=n;i++){
p=lower_bound(b+1,b+q+1,a[i])-b;
rt[i]=modify(rt[i-1],1,q);}
while(m--){
cin>>l>>r>>k;
ans=query(rt[l-1],rt[r],1,q,k);
cout<<b[ans]<<endl;}
return 0;
}
左偏树
如题,一开始有 nnn 个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
1 x y:将第 xxx 个数和第 yyy 个数所在的小根堆合并(若第 xxx 或第 yyy 个数已经被删除或第 xxx 和第 yyy 个数在用一个堆内,则无视此操作)。
2 x:输出第 xxx 个数所在的堆最小数,并将这个最小数删除(若有多个最小数,优先删除先输入的;若第 xxx 个数已经被删除,则输出 −1-1−1 并无视删除操作)。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
}
return fl?-x:x;
}
const int maxn = 1e5 + 10;
int n,m,val[maxn],dis[maxn],fa[maxn];
int ls[maxn],rs[maxn];
int merge(int x,int y){
if(!x || !y)return x|y;
if(val[x]>val[y])swap(x,y);
rs[x]=merge(rs[x],y);
fa[rs[x]]=x;
if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
inline int find(int x){
while(x!=fa[x])x=fa[x]=fa[fa[x]];
return x;
}
#define read() read<int>()
int main(){
n=read();m=read();
for(int i=1;i<=n;i++)val[i]=read();
for(int i=1;i<=n;i++)fa[i]=i;
while(m--){
int op=read();
if(op==1){
int x=read(),y=read();
if(!val[x] || !val[y])continue;
x=find(x);y=find(y);
if(x!=y)fa[x]=fa[y]=merge(x,y);
}
else {
int x=read();
if(!val[x]){
puts("-1");continue;
}
x=find(x);
printf("%d\n",val[x]);
val[x]=0;
fa[ls[x]]=ls[x];fa[rs[x]]=rs[x];
fa[x]=merge(ls[x],rs[x]);
}
}
return 0;
}
点分治
树的重心
点分治
动态规划
初步
单调队列优化
斜率优化
数位dp
区间dp
背包
树上问题
字符串
哈希
一维哈希
自然溢出和大模数
双哈希
二维哈希
k叉树
荷马史诗
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include"bits/stdc++.h"
using namespace std;
#define ll long long
struct node{
ll w,h;
node(ll w,ll h):w(w),h(h){}
bool operator < (const node &a)const{
return w==a.w?h>a.h:w>a.w;
}
};
ll ans;
priority_queue <node> q;
int main(){
ios::sync_with_stdio(false);
ll n,k,ans=0;
cin>>n>>k;
for(int i=1;i<=n;i++){
ll w;
cin>>w;
q.push((node){w,1});
}
while((q.size()-1)%(k-1)!=0)q.push((node){0,1});
while(q.size()>=k){
ll h=-1,w=0;
for(int i=1;i<=k;i++){
node t=q.top();
q.pop();
h=max(h,t.h);
w+=t.w;
}
ans+=w;
q.push((node){w,h+1});
}
cout<<ans<<endl<<q.top().h-1<<endl;
return 0;
}
KMP算法
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int la,lb,nex[N],f[N];
char a[N],b[N];
int main(){
cin>>(a+1);cin>>(b+1);
la=strlen(a+1);lb=strlen(b+1);f[1]=0;
for(int i=2,j=0;i<=lb;i++){
while(j>0&&b[i]!=b[j+1])j=nex[j];
if(b[i]==b[j+1])j++;
nex[i]=j;
}
for(int i=1,j=0;i<=la;i++){
while(j>0&&(j==lb||a[i]!=b[j+1]))j=nex[j];
if(a[i]==b[j+1])j++;
f[i]=j;
if(f[i]==lb){printf("%d\n",i-lb+1);}
}
for(int i=1;i<=lb;i++)printf("%d ",nex[i]);
return 0;
}
manachaer
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=11000005;
char s[N],s_new[N<<1];
int p[N<<1];
inline int init(){
int len=strlen(s);
s_new[0]='$';s_new[1]='#';
int j=2;
for(int i=0;i<len;i++){s_new[j++]=s[i];s_new[j++]='#';}
s_new[j]='\0';
return j;
}
int manacher(){
int len=init(),max_len=-1,id,mx=0;
for(int i=1;i<=len;i++){
if(i<mx)p[i]=min(p[2*id-i],mx-i);
else p[i]=1;
while(s_new[i-p[i]]==s_new[i+p[i]])p[i]++;
if(mx<i+p[i]){id=i;mx=i+p[i];}
max_len=max(max_len,p[i]-1);
}
return max_len;
}
int main(){
cin>>s;
printf("%d",manacher());
return 0;
}
trie树
01 trie
26 trie
AC自动机
有 NNN 个由小写字母组成的模式串以及一个文本串 TTT。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 TTT 中出现的次数最多。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
struct trie{int fail,v[26],end;}t[N];
struct result{
int num,pos;
bool operator <(const result &other)const{return num==other.num?pos<other.pos:num>other.num;}
}ans[N];
int cnt,n;
string s[155];
inline void build(string s,int num){
int l=s.length(),now=0;
for(int i=0;i<l;i++){
if(t[now].v[s[i]-'a']==0)t[now].v[s[i]-'a']=++cnt;
now=t[now].v[s[i]-'a'];
}
t[now].end=num;
}
void GF(){
queue<int> q;
for(int i=0;i<26;i++)
if(t[0].v[i]!=0){t[t[0].v[i]].fail=0;q.push(t[0].v[i]);}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(t[u].v[i]!=0){t[t[u].v[i]].fail=t[t[u].fail].v[i];q.push(t[u].v[i]);}
else t[u].v[i]=t[t[u].fail].v[i];
}
}
void AC(string s){
int l=s.length(),now=0;
for(int i=0;i<l;i++){
now=t[now].v[s[i]-'a'];
for(int j=now;j;j=t[j].fail)ans[t[j].end].num++;
}
}
int main(){
while(cin>>n){
if(!n) break;
cnt=0;memset(t,0,sizeof(t));
for(int i=1;i<=n;i++){
cin>>s[i];
ans[i].num=0;ans[i].pos=i;
build(s[i],i);
}
t[0].fail=0;GF();
cin>>s[0];
AC(s[0]);sort(ans+1,ans+1+n);
cout<<ans[1].num<<endl<<s[ans[1].pos]<<endl;
for(int i=2;i<=n;i++)
if(ans[i].num==ans[i-1].num)cout<<s[ans[i].pos]<<endl;
else break;
}
return 0;
}
给定 nnn 个模式串 sis_isi 和一个文本串 ttt,求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。
\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int n,T,cnt;
string s;
struct trie{int f,v[26],end;}t[N];
inline void build(string s){
int l=s.length(),now=0;
for(int i=0;i<l;i++){
if(t[now].v[s[i]-'a']==0)t[now].v[s[i]-'a']=++cnt;
now=t[now].v[s[i]-'a'];
}
t[now].end+=1;
}
void GF(){
queue<int>q;
for(int i=0;i<26;i++)
if(t[0].v[i]!=0){t[t[0].v[i]].f=0;q.push(t[0].v[i]);}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(t[u].v[i]!=0){t[t[u].v[i]].f=t[t[u].f].v[i];q.push(t[u].v[i]);}
else t[u].v[i]=t[t[u].f].v[i];
}
}
int AC(string s){
int l=s.length(),now=0,ans=0;
for(int i=0;i<l;i++){
now=t[now].v[s[i]-'a'];
for(int j=now;j && t[j].end!=-1;j=t[i].f)
{ans+=t[j].end;t[j].end=-1;}
}
return ans;
}
int main(){
//cin>>T;
//while(T--){
cin>>n;
for(int i=1;i<=n;i++){cin>>s;build(s);}
GF();
cin>>s;
cout<<AC(s)<<endl;
memset(t,0,sizeof(t));cnt=0;
//}
return 0;
}