SDOI2019 省选前模板整理
代码不能折叠,不过就这样吧。有目录也还好。
wdm好多。。
可能要下辈子才能全填上✔了。✘✘✘
Update:模板不复习,考场两行泪。
我要是复习MillerRabin和快速乘就多十八分了啊TAT
不过感谢复习了可持久化Trie。
计算几何✔
最近才写了,过了过了。虽然应该忘得差不多了。
旋转卡壳:经常要找和当前直线平行的直线切在哪里,注意用底相同,平行线之间高也相同,以及叉积有正负的性质,求叉积。
DP
斜率优化✔
将DP式子写成\(y_j=kx_j+b\)的形式,其中\(k\)是与\(i\)有关的系数,\(b\)是要求的\(f_i\)及\(i\)的其他项。
四边形不等式✔
若\(f\)满足四边形不等式,则决策单调,有\(P[i][j-1]\leq P[i][j]\leq P[i+1][j]\)或\(P[i-1][j]\leq P[i][j]\leq P[i][j+1]\),可根据需要使用。
具体看这里。
实际上更大的用处是水过某些DP题...
轮廓线DP✘
nmd这东西根本写不出来的啊。
各种分治
CDQ分治✔
//6196KB 688MS(308ms 5560KB)
//https://www.cnblogs.com/SovietPower/p/8574905.html
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,M=2e5+5;
int Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Node
{
int x,y,z,cnt,ans;
bool operator <(const Node &a)const
{
return x==a.x?(y==a.y?z<a.z:y<a.y):x<a.x;
}
bool operator !=(const Node &a)const
{
return x!=a.x||y!=a.y||z!=a.z;
}
}q[N],tmp[N];
struct BIT
{
int n,t[M];//M!
#define lb(x) (x&-x)
inline void Add(int p,int v)
{
for(; p<=n; p+=lb(p)) t[p]+=v;
}
inline int Query(int p)
{
int res=0;
for(; p; p^=lb(p)) res+=t[p];
return res;
}
inline void Clear(int p)
{
for(; p<=n&&t[p]; p+=lb(p)) t[p]=0;
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
void CDQ(int l,int r)
{
if(l<r)
{
int m=l+r>>1; CDQ(l,m), CDQ(m+1,r);
int p1=l,p2=m+1,p=l;
while(p1<=m&&p2<=r)
{
if(q[p1].y<=q[p2].y) T.Add(q[p1].z,q[p1].cnt), tmp[p++]=q[p1++];//q[p1].cnt!
else q[p2].ans+=T.Query(q[p2].z), tmp[p++]=q[p2++];
}
while(p2<=r) q[p2].ans+=T.Query(q[p2].z), tmp[p++]=q[p2++];
for(int i=l; i<p1; ++i) T.Clear(q[i].z);//<p1
while(p1<=m) tmp[p++]=q[p1++];
for(int i=l; i<=r; ++i) q[i]=tmp[i];
}
}
int main()
{
int n=read(); T.n=read();
for(int i=1; i<=n; ++i) q[i]=(Node){read(),read(),read(),1,0};//cnt不能是0啊= =
std::sort(q+1,q+1+n); int cnt=1;
for(int i=2; i<=n; ++i)
if(q[i]!=q[i-1]) q[++cnt]=q[i];
else ++q[cnt].cnt;
CDQ(1,cnt);
for(int i=1; i<=cnt; ++i) Ans[q[i].ans+q[i].cnt-1]+=q[i].cnt;
for(int i=0; i<n; ++i) printf("%d\n",Ans[i]);
return 0;
}
点分治✔
过。
整体二分✔
//2740kb 132ms
//https://www.cnblogs.com/SovietPower/p/9231606.html
//原数列的值,要在树状数组上减掉啊。
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 400000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,M=N*3;
int A[N],Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Operation
{
int l,r,k,id;//id=0:A[l]=r 变化量为k
}q[M];
struct BIT
{
int n,t[N];
#define lb(x) (x&-x)
inline void Add(int p,int v)
{
for(; p<=n; p+=lb(p)) t[p]+=v;
}
inline int Query(int p)
{
int res=0;
for(; p; p^=lb(p)) res+=t[p];
return res;
}
inline void Clear(int p)
{
for(; p<=n&&t[p]; p+=lb(p)) t[p]=0;
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline char GetOpt()
{
register char c=gc(); while(c!='Q'&&c!='C') c=gc();
return c;
}
void Solve(int l,int r,int h,int t)
{
static Operation q1[M],q2[M];
if(h>t) return;
if(l==r)
{
for(int i=h; i<=t; ++i) Ans[q[i].id]=l;
return;
}
bool fg=0;
for(int i=h; i<=t; ++i) if(q[i].id) {fg=1; break;}
if(!fg) return;
int m=l+r>>1,t1=0,t2=0;
for(int i=h; i<=t; ++i)
if(q[i].id)
{
int tmp=T.Query(q[i].r)-T.Query(q[i].l-1);
if(tmp>=q[i].k) q1[t1++]=q[i];
else q[i].k-=tmp, q2[t2++]=q[i];
}
else if(q[i].r<=m) T.Add(q[i].l,q[i].k), q1[t1++]=q[i];
else q2[t2++]=q[i];
for(int i=h; i<=t; ++i) if(!q[i].id&&q[i].r<=m) T.Clear(q[i].l);
for(int i=h,p=0; p<t1; q[i++]=q1[p++]);
for(int i=h+t1,p=0; p<t2; q[i++]=q2[p++]);
Solve(l,m,h,h+t1-1), Solve(m+1,r,h+t1,t);
}
int main()
{
int n=read(),Q=read(),tot=0,now=n,mn=1e9,mx=0;
for(int i=1; i<=n; ++i) q[i]=(Operation){i,A[i]=read(),1,0},mx=std::max(mx,A[i]),mn=std::min(mn,A[i]);
for(int i=1,p; i<=Q; ++i)
if(GetOpt()=='Q') q[++now]=(Operation){read(),read(),read(),++tot};
else p=read(),q[++now]=(Operation){p,A[p],-1,0},q[++now]=(Operation){p,A[p]=read(),1,0},mx=std::max(mx,A[p]),mn=std::min(mn,A[p]);//修改原数列!
T.n=n, Solve(mn,mx,1,now);
for(int i=1; i<=tot; ++i) printf("%d\n",Ans[i]);
return 0;
}
数据结构
线段树、树状数组、Trie树、树剖、二维线段树、主席树略
树分块...我还不会卡不掉的树分块。。。
线段树合并✔
Merge
的时候不要写Update(x)
就好了...
分块✔
查询区间\(k\)的倍数时,对于整块加\(val\),可以变成求这块模\(k=k-val\)的数的个数,这样只需要维护一个模数标记(初始为\(0\)),这样就将整块加转化成了标记的\(O(1)\)修改。
这个思路挺好的,应该能拓展。
K-D Tree
LCT
http://www.cnblogs.com/SovietPower/category/1182810.html
//520ms 2984KB
//https://www.cnblogs.com/SovietPower/p/8615938.html
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=3e5+5;
char IN[MAXIN],*SS=IN,*TT=IN;
struct LCT
{
#define ls son[x][0]
#define rs son[x][1]
int fa[N],son[N][2],sum[N],val[N],sk[N];
bool rev[N];
inline void Update(int x)
{
sum[x]=sum[ls]^sum[rs]^val[x];
}
inline bool n_root(int x)
{
return son[fa[x]][0]==x||son[fa[x]][1]==x;
}
inline void Rev(int x)
{
std::swap(ls,rs), rev[x]^=1;
}
inline void PushDown(int x)
{
if(rev[x]) Rev(ls), Rev(rs), rev[x]=0;
}
void Rotate(int x)
{
int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
if(n_root(a)) son[b][son[b][1]==a]=x;
if(son[x][r]) fa[son[x][r]]=a;
fa[x]=b, fa[a]=x, son[a][l]=son[x][r], son[x][r]=a;
Update(a);
}
void Splay(int x)
{
int t=1,a=x; sk[1]=a;
while(n_root(a)) sk[++t]=a=fa[a];
while(t) PushDown(sk[t--]);
while(n_root(x))
{
if(n_root(a=fa[x])) Rotate(son[a][0]==x^son[fa[a]][0]==a?x:a);
Rotate(x);
}
Update(x);
}
void Access(int x)
{
for(int pre=0; x; x=fa[pre=x])
Splay(x), rs=pre, Update(x);
}
void MakeRoot(int x)
{
Access(x), Splay(x), Rev(x);
}
void Split(int x,int y)
{
MakeRoot(x), Access(y), Splay(y);
}
int FindRoot(int x)
{
Access(x), Splay(x);
while(ls) x=ls;
return x;
}
void Link(int x,int y)
{
MakeRoot(x);
if(FindRoot(y)!=x) fa[x]=y;
}
void Cut(int x,int y)
{
MakeRoot(x);
if(FindRoot(y)==x&&fa[x]==y&&!rs) fa[x]=son[y][0]=0, Update(y);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
int main()
{
int n=read(),q=read();
for(int i=1; i<=n; ++i) T.val[i]=read();
for(int x,y; q--; )
switch(read())
{
case 0: x=read(),y=read(),T.Split(x,y),printf("%d\n",T.sum[y]); break;
case 1: x=read(),y=read(),T.Link(x,y); break;
case 2: x=read(),y=read(),T.Cut(x,y); break;
case 3: T.Splay(x=read()),T.val[x]=read(); break;
}
return 0;
}
可持久化Trie✔
https://i.cnblogs.com/posts?categoryid=1161664
Splay
http://www.cnblogs.com/SovietPower/p/8435011.html
fhq Treap
http://www.cnblogs.com/SovietPower/p/8431909.html
虚树
可并堆 左偏树*
http://www.cnblogs.com/SovietPower/p/8435041.html
数学,数论
CRT
扩展CRT
Lucas
扩展Lucas
杜教筛✔
Min25筛
莫比乌斯反演✔
FFT,NTT✔
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=(1<<21)+5;
const double PI=acos(-1);
int rev[N];
struct Complex
{
double x,y;
Complex(double x=0,double y=0):x(x),y(y) {}
inline Complex operator +(const Complex &a)const {return Complex(x+a.x, y+a.y);}
inline Complex operator -(const Complex &a)const {return Complex(x-a.x, y-a.y);}
inline Complex operator *(const Complex &a)const {return Complex(x*a.x-y*a.y, x*a.y+y*a.x);}
}f[N],g[N],W[N];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
void FFT(Complex *a,int lim,int opt)
{
static Complex w[N];
for(int i=0; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1;
Complex Wn(cos(PI/mid),opt*sin(PI/mid));
for(int j=0; j<lim; j+=i)
{
Complex w(1,0),t;
for(int k=j; k<j+mid; ++k,w=w*Wn)
a[k+mid]=a[k]-(t=w*a[k+mid]), a[k]=a[k]+t;
}
// if(~opt) for(int k=0,t=lim/i; k<mid; ++k) w[k]=W[k*t];
// else for(int k=0,t=lim/i; k<mid; ++k) w[k]=Complex(W[k*t].x,-W[k*t].y);
// for(int j=0; j<lim; j+=i)
// {
// Complex t;
// for(int k=j; k<j+mid; ++k)
// a[k+mid]=a[k]-(t=w[k-j]*a[k+mid]), a[k]=a[k]+t;
// }
}
if(opt==-1) for(int i=0; i<lim; ++i) a[i].x/=lim;
}
int main()
{
int n=read()+1,m=read()+1;
for(int i=0; i<n; ++i) f[i].x=read();
for(int i=0; i<m; ++i) g[i].x=read();
int lim=1,bit=-1;
while(lim<=n+m) lim<<=1,++bit;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit);
// for(int i=0,t=lim>>1; i<lim; ++i) W[i]=Complex(cos(i*PI/t),sin(i*PI/t));
FFT(f,lim,1), FFT(g,lim,1);
for(int i=0; i<lim; ++i) f[i]=f[i]*g[i];
FFT(f,lim,-1);
for(int i=0,l=n+m-2; i<=l; ++i) printf("%d ",int(f[i].x+0.5));
return 0;
}
#include <cstdio>
#include <cctype>
#include <algorithm>
#define mod 998244353
#define G 3
#define invG 332748118
#define Mod(x) x>=mod&&(x-=mod)
#define gc() getchar()
typedef long long LL;
const int N=(1<<21)+5;
int f[N],g[N],rev[N];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline int FP(int x,int k)
{
int t=1;
for(; k; k>>=1,x=1ll*x*x%mod) k&1&&(t=1ll*t*x%mod);
return t;
}
void NTT(int *a,int lim,int opt)
{
for(int i=0; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1; LL Wn=FP(~opt?G:invG,(mod-1)/i);
for(int j=0; j<lim; j+=i)
for(int k=j,t,w=1; k<j+mid; ++k,w=w*Wn%mod)
a[k+mid]=a[k]+mod-(t=1ll*w*a[k+mid]%mod), Mod(a[k+mid]), a[k]+=t, Mod(a[k]);
}
if(opt==-1) for(int i=0,inv=FP(lim,mod-2); i<lim; ++i) a[i]=1ll*a[i]*inv%mod;
}
int main()
{
int n=read()+1,m=read()+1;
for(int i=0; i<n; ++i) f[i]=read();
for(int i=0; i<m; ++i) g[i]=read();
int lim=1,bit=-1;
while(lim<=n+m) lim<<=1,++bit;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit);
NTT(f,lim,1), NTT(g,lim,1);
for(int i=0; i<lim; ++i) f[i]=1ll*f[i]*g[i]%mod;
NTT(f,lim,-1);
for(int i=0,l=n+m-2; i<=l; ++i) printf("%d ",f[i]);
return 0;
}
FWT✔
BSGS
Miller Rabin*
Pollard Rho*
Catalan数 Stirling数
高斯消元
拉格朗日插值✔
单纯形*
线性基
博弈论
图论
最短路、生成树、次小生成树略。
Prufer序列
Tarjan
差分约束
二分图匹配
欧拉路、哈密顿路
圆方树
支配树*
2-SAT*
Matrix Tree*
http://www.cnblogs.com/SovietPower/p/8463968.html
网络流
Dinic、ISAP、费用流略。
无源汇上下界网络流✔
有源汇上下界网络流✔
有源汇上下界最小流✔
字符串
KMP✔
https://www.luogu.org/recordnew/show/15171303
Manacher✔
http://www.cnblogs.com/SovietPower/p/8677979.html
回文树✔
//110ms 34876KB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=3e5+5;
struct PAM
{
int tot,las,fail[N],len[N],val[N],son[N][26];
char s[N];
PAM() {tot=1, las=0, fail[0]=1, len[1]=-1, s[0]='$';}
inline int Find(int x,int n)
{
while(s[n-len[x]-1]!=s[n]) x=fail[x];
return x;
}
void Insert(int c,int n)
{
int p=Find(las,n);
if(!son[p][c])
{
int np=++tot; fail[np]=son[Find(fail[p],n)][c];
son[p][c]=np, len[np]=len[p]+2;
}
++val[las=son[p][c]];
}
void Solve()
{
scanf("%s",s+1); int n=strlen(s+1);
for(int i=1; i<=n; ++i) Insert(s[i]-'a',i);
LL ans=0;
for(int x=tot; x>1; --x)
ans=std::max(ans,1ll*val[x]*len[x]), val[fail[x]]+=val[x];
printf("%lld\n",ans);
}
}pam;
int main()
{
pam.Solve();
return 0;
}
后缀数组✔
//297ms 15228kb
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=1e6+5;
struct Suffix_Array
{
int rk[N],sa[N],sa2[N],tm[N],ht[N];
char s[N];
void Build(char *s,int n,int m)
{
int *x=rk,*y=sa2;
for(int i=0; i<=m; ++i) tm[i]=0;
for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]-'a'+1];
for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
for(int i=n; i; --i) sa[tm[x[i]]--]=i;
for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
{
for(int i=n-k+1; i<=n; ++i) y[++p]=i;
for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;
for(int i=1; i<=m; ++i) tm[i]=0;
for(int i=1; i<=n; ++i) ++tm[x[i]];
for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];
std::swap(x,y), x[sa[1]]=p=1;
for(int i=2; i<=n; ++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
if(p>=n) break;
}
for(int i=1; i<=n; ++i) rk[sa[i]]=i;
ht[1]=0;
for(int i=1,k=0; i<=n; ++i)
{
if(rk[i]==1) continue;
if(k) --k;
int p=sa[rk[i]-1];
while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;
ht[rk[i]]=k;
}
}
void Solve()
{
scanf("%s",s+1); int n=strlen(s+1);
Build(s,n,27);
for(int i=1; i<=n; ++i) printf("%d ",sa[i]); puts("");
for(int i=2; i<=n; ++i) printf("%d ",ht[i]); puts("");
}
}sa;
int main()
{
sa.Solve();
return 0;
}
AC自动机
http://www.cnblogs.com/SovietPower/p/8530327.html
后缀自动机
其它
dsu on tree
长链剖分
高维前缀和✔
带权二分
模拟退火
一般莫队✔
常搭配分块使用(莫队本身\(O(n\sqrt n)\)次修改,\(O(n)\)次查询)。
优化什么的(分奇偶),不管惹。
带修改莫队
回滚莫队✔
还是将所有询问按\((左端点所在块,\ 右端点)\)排序。对于左右端点在同一块里的询问,暴力查询。
否则,把左端点在同一块\(i\)里的询问一起处理。这时右端点一定是递增的。
令\(l\)为第\(i+1\)块(下一块)的左端点,\(r\)为第\(i\)块(该块)的右端点。将\(r\)移动到当前询问的右端点处并更新答案,记录此时答案\(tmp=Now\)。
将\(l\)移动到当前询问的左端点处并更新答案,得到当前询问的答案\(Now\)。
将\(l\)移回到下一块左端点位置,并消除影响(减掉加的次数之类的),并令\(Now=tmp\)。
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------