把一些感觉不太熟的模板再敲一敲吧。
source:「HihoCoder 1183」割边与割点
#include
using namespace std;
typedef pair<int,int> pii;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=2e4+5,M=1e5+5;
int n,m,tot,root;
int dfn[N],low[N],cut[N],bridge[M<<1];
int t=1,first[N],v[M<<1],nxt[M<<1];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Max(int &x,int y) {if(x<y)x=y;}
void Min(int &x,int y) {if(x>y)x=y;}
void Clear() {tot=0,memset(dfn,0,sizeof(dfn));}
void Tarjan_cut(int x,int son=0){
dfn[x]=low[x]=++tot;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(!dfn[to]){
son++,Tarjan_cut(to),Min(low[x],low[to]);
cut[x]|=(root==x&&son>1)|(root!=x&&low[to]>=dfn[x]);
}
else Min(low[x],dfn[to]);
}
}
void Tarjan_bridge(int x,int pre){
dfn[x]=low[x]=++tot;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(i==(pre^1)) continue;
if(!dfn[to]){
Tarjan_bridge(to,i),Min(low[x],low[to]);
if(low[to]>dfn[x]) bridge[i]=bridge[i^1]=1;
}
else Min(low[x],dfn[to]);
}
}
vector<int>point;
vector<pii>edges;
int main(){
n=in(),m=in();
for(int i=1,x,y;i<=m;++i) x=in(),y=in(),add(x,y),add(y,x);
Clear(),Tarjan_cut(root=1);
Clear(),Tarjan_bridge(1,0);
for(int i=1;i<=n;++i) if(cut[i]) point.push_back(i);
for(int i=2;i<t;i+=2) if(bridge[i]) edges.push_back((v[i]<v[i^1])?pii(v[i],v[i^1]):pii(v[i^1],v[i]));
sort(edges.begin(),edges.end());
if(point.empty()) puts("Null");
for(int i=0;i<point.size();++i) printf("%d%c",point[i],(i==point.size()-1)?'\n':' ');
for(int i=0;i<edges.size();++i) printf("%d %d\n",edges[i].first,edges[i].second);
return 0;
}
source:「HNOI 2012」矿场修建
解析就去博客里看吧,这里存一下点双的代码。
#include
using namespace std;
typedef long long ll;
const int N=1005;
int n,m,root,tot,num;
int dfn[N],low[N],cut[N],vis[N];
int t,first[N],v[N],nxt[N];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Max(int &x,int y) {if(x<y)x=y;}
void Min(int &x,int y) {if(x>y)x=y;}
void Clear(){
n=t=tot=num=0;
memset(first,0,sizeof(first));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(cut,0,sizeof(cut));
}
void Tarjan(int x,int son=0){
dfn[x]=low[x]=++tot;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(!dfn[to]){
son++,Tarjan(to),Min(low[x],low[to]);
cut[x]|=(root==x&&son>1)|(root!=x&&low[to]>=dfn[x]);
}
else Min(low[x],dfn[to]);
}
}
int cnt_cut,cnt_pnt,ans1;
ll ans2;
void dfs(int x,int id){
vis[x]=id,cnt_pnt++;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(cut[to]&&vis[to]!=id) vis[to]=id,cnt_cut++;
if(!vis[to]) dfs(to,id);
}
}
int main(){
int Case=0;
while((~scanf("%d",&m))&&m){
Clear();
for(int i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
add(x,y),add(y,x),Max(n,max(x,y));
}
for(int i=1;i<=n;++i)
if(!dfn[i]) Tarjan(root=i);
ans1=0,ans2=1;
for(int i=1;i<=n;++i){
if(cut[i]||vis[i]) continue;
cnt_cut=cnt_pnt=0,dfs(i,++num);
if(cnt_cut==0) ans1+=2,ans2*=cnt_pnt*(cnt_pnt-1)/2;
if(cnt_cut==1) ans1+=1,ans2*=cnt_pnt;
}
printf("Case %d: %d %lld\n",++Case,ans1,ans2);
}
return 0;
}
source:「HihoCoder 1184」边的双连通分量
代码和强联通分量出奇的像呢。
#include
using namespace std;
typedef pair<int,int> pii;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=2e4+5,M=1e5+5;
int n,m,tot,top,num;
int dfn[N],low[N],stk[N],id[N],bel[N];
int t=1,first[N],v[M<<1],nxt[M<<1];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Max(int &x,int y) {if(x<y)x=y;}
void Min(int &x,int y) {if(x>y)x=y;}
void Tarjan(int x,int pre){
dfn[x]=low[x]=++tot,stk[++top]=x;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(i==(pre^1)) continue;
if(!dfn[to]) Tarjan(to,i),Min(low[x],low[to]);
else Min(low[x],dfn[to]);
}
int k;
if(dfn[x]==low[x]){
id[++num]=n+1;
do{
k=stk[top--],Min(id[num],k),bel[k]=num;
}while(k!=x);
}
}
int main(){
n=in(),m=in();
for(int i=1,x,y;i<=m;++i) x=in(),y=in(),add(x,y),add(y,x);
Tarjan(1,0),printf("%d\n",num);
for(int i=1;i<=n;++i) printf("%d ",id[bel[i]]);
return 0;
}
source:「校内网站 3784」树链剖分换根
复习一下换根的模板,分析可以看这里。
PS:换根是对链修改,链查询没有影响的,虽然这里用不着。
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
using IO::gc;
const int N=1e5+5;
int n,Q,root=1;
int t,val[N],first[N],v[N],nxt[N];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
int tot,fa[N],dep[N],son[N],sze[N],top[N],pos[N],idx[N];
namespace SGT{
int mn[N<<2];
#define mid ((l+r)>>1)
void build(int root,int l,int r){
if(l==r) {mn[root]=val[idx[l]];return;}
build(root<<1,l,mid),build(root<<1|1,mid+1,r);
mn[root]=min(mn[root<<1],mn[root<<1|1]);
}
void Modify(int root,int l,int r,int pos,int val){
if(l==r) {mn[root]=val;return;}
if(pos<=mid) Modify(root<<1,l,mid,pos,val);
else Modify(root<<1|1,mid+1,r,pos,val);
mn[root]=min(mn[root<<1],mn[root<<1|1]);
}
int Query(int root,int l,int r,int x,int y){
if(x>y) return 2e9;
if(l>=x&&r<=y) return mn[root];
if(y<=mid) return Query(root<<1,l,mid,x,y);
if(x> mid) return Query(root<<1|1,mid+1,r,x,y);
return min(Query(root<<1,l,mid,x,y),Query(root<<1|1,mid+1,r,x,y));
}
#undef mid
}
namespace Tree_cutting{
void dfs1(int x){
sze[x]=1;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
fa[to]=x,dep[to]=dep[x]+1;
dfs1(to),sze[x]+=sze[to];
if(sze[to]>sze[son[x]]) son[x]=to;
}
}
void dfs2(int x,int tp){
top[x]=tp,idx[pos[x]=++tot]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=first[x];i;i=nxt[i])
if(v[i]!=son[x]) dfs2(v[i],v[i]);
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int find(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
if(fa[top[x]]==y) return top[x];
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return son[x];
}
}
using namespace Tree_cutting;
int main(){
n=in(),Q=in();
for(int i=1,fa;i<=n;++i){
fa=in(),val[i]=in();
if(fa) add(fa,i);
}
dfs1(1),dfs2(1,1),SGT::build(1,1,n);
int x,y;
while(Q--){
char op=gc();
while(op!='E'&&op!='V'&&op!='Q') op=gc();
if(op=='E') root=in();
else if(op=='V') x=in(),y=in(),SGT::Modify(1,1,n,pos[x],y);
else{
x=in();
if(x==root) {printf("%d\n",SGT::mn[1]);continue;}
if(x!=LCA(x,root)) {printf("%d\n",SGT::Query(1,1,n,pos[x],pos[x]+sze[x]-1));continue;}
int child=find(x,root);
printf("%d\n",min(SGT::Query(1,1,n,1,pos[child]-1),SGT::Query(1,1,n,pos[child]+sze[child],n)));
}
}
return 0;
}
source:「洛谷 3865」ST表
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m,Log[N],mx[N][18];
void prework(){
for(int i=2;i<=n;++i) Log[i]=Log[i>>1]+1;
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i+(1<<(j-1))<=n;++i)
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
}
int GetMax(int l,int r){
int k=Log[r-l+1];
return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int main(){
n=in(),m=in();
for(int i=1;i<=n;++i) mx[i][0]=in();
prework();
while(m--){
int l=in(),r=in();
printf("%d\n",GetMax(l,r));
}
return 0;
}
source:「洛谷 3377」左偏树
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m;
int fa[N],lc[N],rc[N],dis[N],val[N];
int find(int x) {return (fa[x]==x)?x:fa[x]=find(fa[x]);}
int Merge(int x,int y){
if(!x||!y) return x+y;
if(val[x]>val[y]) swap(x,y);
rc[x]=Merge(rc[x],y);
if(dis[lc[x]]<dis[rc[x]]) swap(lc[x],rc[x]);
fa[lc[x]]=fa[rc[x]]=x,dis[x]=dis[rc[x]]+1;
return x;
}
void Pop(int x){
val[x]=-1;
fa[lc[x]]=lc[x],fa[rc[x]]=rc[x];
fa[x]=Merge(lc[x],rc[x]);
}
int main(){
n=in(),m=in();
for(int i=1;i<=n;++i) fa[i]=i,val[i]=in();
while(m--){
int op=in();
if(op==1){
int x=in(),y=in();
if(val[x]==-1||val[y]==-1||find(x)==find(y)) continue;
x=find(x),y=find(y),Merge(x,y);
}
else{
int x=in();
if(val[x]==-1) {printf("-1\n");continue;}
x=find(x),printf("%d\n",val[x]),Pop(x);
}
}
return 0;
}
source:「洛谷 3369」普通平衡树
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n;
namespace FHQ{
int root,tot,sze[N],lc[N],rc[N],val[N],key[N];
int newnode(int v){
int u=++tot;
sze[u]=1,lc[u]=rc[u]=0,val[u]=v,key[u]=rand();
return u;
}
void Split(int root,int &r1,int &r2,int k){
if(!root) {r1=r2=0;return;}
if(val[root]<=k) r1=root,Split(rc[root],rc[r1],r2,k);
else r2=root,Split(lc[root],r1,lc[r2],k);
sze[root]=sze[lc[root]]+sze[rc[root]]+1;
}
void Merge(int &root,int r1,int r2){
if(!r1||!r2) {root=r1+r2;return;}
if(key[r1]<key[r2]) root=r1,Merge(rc[root],rc[r1],r2);
else root=r2,Merge(lc[root],r1,lc[r2]);
sze[root]=sze[lc[root]]+sze[rc[root]]+1;
}
void Insert(int k){
int r1=0,r2=0;
int x=newnode(k);
Split(root,r1,r2,k);
Merge(r1,r1,x);
Merge(root,r1,r2);
}
void Delete(int k){
int r1=0,r2=0,r3=0;
Split(root,r1,r2,k);
Split(r1,r1,r3,k-1);
Merge(r3,lc[r3],rc[r3]);
Merge(r1,r1,r3);
Merge(root,r1,r2);
}
int Rank(int k){
int r1=0,r2=0;
Split(root,r1,r2,k-1);
int ans=sze[r1]+1;
Merge(root,r1,r2);
return ans;
}
int find(int root,int k){
if(!root) return 0;
if(k==sze[lc[root]]+1) return root;
if(k<sze[lc[root]]+1) return find(lc[root],k);
return find(rc[root],k-sze[lc[root]]-1);
}
int Val(int x) {return val[find(root,x)];}
int Prefix(int x){
int r1=0,r2=0;
Split(root,r1,r2,x-1);
int pos=find(r1,sze[r1]);
Merge(root,r1,r2);
return val[pos];
}
int Suffix(int x){
int r1=0,r2=0;
Split(root,r1,r2,x);
int pos=find(r2,1);
Merge(root,r1,r2);
return val[pos];
}
}
int main(){
n=in();
srand(time(0));
while(n--){
int op=in(),x=in();
if(op==1) FHQ::Insert(x);
else if(op==2) FHQ::Delete(x);
else if(op==3) printf("%d\n",FHQ::Rank(x));
else if(op==4) printf("%d\n",FHQ::Val(x));
else if(op==5) printf("%d\n",FHQ::Prefix(x));
else printf("%d\n",FHQ::Suffix(x));
}
return 0;
}
source:「洛谷 3690」Link Cut Tree
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m;
namespace LCT{
int fa[N],son[N][2],Xor[N],val[N],mark[N];
inline bool Get(int x) {return son[fa[x]][1]==x;}
inline bool isroot(int x) {return (!fa[x])||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x);}
inline void pushup(int x) {Xor[x]=Xor[son[x][0]]^Xor[son[x][1]]^val[x];}
inline void pushdown(int x){
if(!mark[x]) return;
swap(son[x][0],son[x][1]);
if(son[x][0]) mark[son[x][0]]^=1;
if(son[x][1]) mark[son[x][1]]^=1;
mark[x]=0;
}
inline void Rotate(int x){
int y=fa[x],z=fa[y],k=Get(x),l=son[x][k^1];
son[y][k]=l,fa[l]=(l?y:0);
if(!isroot(y)) son[z][Get(y)]=x;fa[x]=z;
son[x][k^1]=y,fa[y]=x;
pushup(y),pushup(x);
}
int stk[N],top;
inline void Splay(int x){
stk[top=1]=x;
for(int i=x;!isroot(i);i=fa[i]) stk[++top]=fa[i];
while(top) pushdown(stk[top]),top--;
while(!isroot(x)){
int y=fa[x];
if(!isroot(y)) Rotate(Get(x)==Get(y)?y:x);
Rotate(x);
}
}
inline void Access(int x){
for(int i=0;x;x=fa[i=x])
Splay(x),son[x][1]=i,pushup(x);
}
inline int Findroot(int x){
Access(x),Splay(x);
while(pushdown(x),son[x][0]) x=son[x][0];
return Splay(x),x;
}
inline void Makeroot(int x) {Access(x),Splay(x),mark[x]^=1;}
inline void Path(int x,int y) {Makeroot(x),Access(y),Splay(y);}
inline void Link(int x,int y){
if(Findroot(x)==Findroot(y)) return;
Makeroot(x),fa[x]=y;
}
inline void Cut(int x,int y){
if(Findroot(x)!=Findroot(y)) return;
Path(x,y);
if(!fa[x]||son[x][1]) return;
fa[x]=0,son[y][0]=0,pushup(y);
}
}
int main(){
n=in(),m=in();
for(int i=1;i<=n;++i) LCT::val[i]=in();
while(m--){
int op=in(),x=in(),y=in();
if(!op) LCT::Path(x,y),printf("%d\n",LCT::Xor[y]);
else if(op==1) LCT::Link(x,y);
else if(op==2) LCT::Cut(x,y);
else LCT::Splay(x),LCT::val[x]=y,LCT::pushup(x);
}
return 0;
}
source:「洛谷 5357」AC自动机
感觉是挺好的一道模板题。
在 AC 自动机上每匹配到一个节点,实际上从 fail 到根都能被匹配到一次。因此先把 fail 树建出来,链加就用差分维护,由于都是到根的链加,因此我们只用在当前点打个标记即可。
注意读入的串有相同的。
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int gi() {return Read<int>();}
inline int gs(char *S){
char c=gc();int len=0;
while(!isalpha(c)) c=gc();
while( isalpha(c)) S[++len]=c,c=gc();
return S[len+1]='\0',len;
}
}
using IO::gi;
using IO::gs;
const int N=2e5+5;
int n,l,tot,End[N],Tag[N];
struct Trie{
int fail,son[26];
}a[N];
char S[N*10];
int t,first[N],v[N],nxt[N];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Insert(int id,char *S){
int p=0;
for(int i=1;i<=l;++i){
if(!a[p].son[S[i]-'a'])
a[p].son[S[i]-'a']=++tot;
p=a[p].son[S[i]-'a'];
}
End[id]=p;
}
queue<int>Q;
void Get_fail(){
for(int i=0;i<26;++i)
if(a[0].son[i]) Q.push(a[0].son[i]);
while(!Q.empty()){
int x=Q.front();Q.pop();
add(a[x].fail,x);
for(int i=0;i<26;++i){
if(a[x].son[i]) a[a[x].son[i]].fail=a[a[x].fail].son[i],Q.push(a[x].son[i]);
else a[x].son[i]=a[a[x].fail].son[i];
}
}
}
void Work(){
int p=0;
for(int i=1;i<=l;++i){
p=a[p].son[S[i]-'a'],Tag[p]++;
}
}
void dfs(int x){
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
dfs(to),Tag[x]+=Tag[to];
}
}
int main(){
n=gi();
for(int i=1;i<=n;++i) l=gs(S),Insert(i,S);
Get_fail(),l=gs(S),Work(),dfs(0);
for(int i=1;i<=n;++i) printf("%d\n",Tag[End[i]]);
return 0;
}
source:「洛谷 3804」后缀自动机
到现在打 SAM 还是觉得好妙啊。
#include
using namespace std;
typedef long long ll;
const int N=2e6+5;
int n,sum[N],Sort[N];
char S[N];
template<typename T>void Max(T &x,T y) {if(x<y)x=y;}
template<typename T>void Min(T &x,T y) {if(x>y)x=y;}
namespace SAM{
int last=1,tot=1;
struct node{
int fa,len,sze,nxt[26];
}a[N];
void Insert(int c){
int p,cur=++tot;
a[cur].len=a[last].len+1,a[cur].sze=1;
for(p=last;p&&!a[p].nxt[c];p=a[p].fa) a[p].nxt[c]=cur;
if(!p) a[cur].fa=1;
else{
int now=a[p].nxt[c];
if(a[now].len==a[p].len+1) a[cur].fa=now;
else{
int Clone=++tot;
a[Clone]=a[now],a[Clone].len=a[p].len+1,a[Clone].sze=0;
for(;p&&a[p].nxt[c]==now;p=a[p].fa) a[p].nxt[c]=Clone;
a[cur].fa=a[now].fa=Clone;
}
}
last=cur;
}
}
using namespace SAM;
int main(){
scanf("%s",S+1),n=strlen(S+1);
for(int i=1;i<=n;++i) SAM::Insert(S[i]-'a');
for(int i=1;i<=tot;++i) sum[a[i].len]++;
for(int i=1;i<=tot;++i) sum[i]+=sum[i-1];
for(int i=1;i<=tot;++i) Sort[sum[a[i].len]--]=i;
ll ans=0;
for(int i=tot;i>=1;--i){
int x=Sort[i];
if(a[x].sze!=1) Max(ans,(ll)a[x].len*a[x].sze);
a[a[x].fa].sze+=a[x].sze;
}
printf("%lld\n",ans);
return 0;
}
source:「洛谷 5496」回文自动机
快忘完了,复习一下。
#include
using namespace std;
const int N=5e5+5;
namespace PAM{
int n,tot,last,S[N],fail[N],len[N],num[N],sze[N],son[N][26];
void init() {tot=1,S[0]=-1,fail[0]=fail[1]=1,len[0]=0,len[1]=-1;}
int Getfail(int x) {while(S[n]!=S[n-len[x]-1])x=fail[x];return x;}
void Insert(int c){
S[++n]=c;
int p=Getfail(last);
if(!son[p][c]){
int now=Getfail(fail[p]);
fail[++tot]=son[now][c],son[p][c]=tot,len[tot]=len[p]+2;
}
last=son[p][c],sze[last]++;
num[last]=num[fail[last]]+1;
}
}
int ans=0;
int main(){
PAM::init();
char c=getchar();
while(!isalpha(c)) c=getchar();
while( isalpha(c)){
PAM::Insert((c-'a'+ans)%26);
printf("%d ",ans=PAM::num[PAM::last]);
c=getchar();
}
return 0;
}
source:「洛谷 4777」扩展中国剩余定理
这个也快忘了。。。
#include
using namespace std;
typedef long long ll;
int n;
ll mul(ll x,ll y,ll p) {return (x*y-(ll)((long double)x/p*y)*p+p)%p;}
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b) {x=1,y=0;return;}
exgcd(b,a%b,y,x),y-=a/b*x;
}
int main(){
ll a1,a2,m1,m2,x,y;
scanf("%d%lld%lld",&n,&m1,&a1);
for(int i=2;i<=n;++i){
scanf("%lld%lld",&m2,&a2);
ll Gcd=gcd(m1,m2),Lcm=m1/Gcd*m2,A=a2-a1;
if(A%Gcd) return puts("-1"),0;
m1/=Gcd,m2/=Gcd,A/=Gcd;
exgcd(m1,m2,x,y),x=(mul(x,A,m2)+m2)%m2;
a1=(a1+m1*Gcd%Lcm*x%Lcm)%Lcm,m1=Lcm;
}
printf("%lld\n",a1);
return 0;
}
source:「CQOI 2018」破解D-H协议
把 unordered_map 换成了哈希表,快的一匹。
#include
using namespace std;
int n,g,P;
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
namespace Hash_Table{
const int N=1e6+10,mod=1e6+3;
int t,first[N],v[N],w[N],nxt[N];
void Clear() {t=0,memset(first,0,sizeof(first));}
void Add(int x,int y,int z) {nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;}
void Insert(int x,int val){
int pos=x%mod;
for(int i=first[pos];i;i=nxt[i])
if(v[i]==x) {w[i]=val;return;}
Add(pos,x,val);
}
int Query(int x){
int pos=x%mod;
for(int i=first[pos];i;i=nxt[i])
if(v[i]==x) return w[i];
return -1;
}
}
using namespace Hash_Table;
int BSGS(int a,int b){
Clear();
int now=b,t=ceil(sqrt(P));
for(int i=0;i<t;++i) Insert(now,i),now=mul(now,a);
if(!a) return b?-1:1;
a=power(a,t),now=1;
for(int i=0;i<=t;++i){
int tmp=Query(now);
if((~tmp)&&i*t-tmp>=0) return i*t-tmp;
now=mul(now,a);
}
}
int main(){
scanf("%d%d%d",&g,&P,&n);
for(int i=1,A,B;i<=n;++i){
scanf("%d%d",&A,&B);
int a=BSGS(g,A),b=BSGS(g,B);
printf("%d\n",power(g,1ll*a*b%(P-1)));
}
return 0;
}
source:「洛谷 3389」高斯消元法
听说洛谷的数据比较水。。
建议做几道模板题测一下模板。
#include
using namespace std;
const int N=105;
int n,flag=1;
double a[N][N],X[N];
void Guass(){
for(int i=1;i<=n;++i){
int k=i;
for(int j=i+1;j<=n;++j)
if(fabs(a[j][i])>fabs(a[k][i])) k=j;
swap(a[i],a[k]);
for(int j=i+1;j<=n;++j)
for(int k=i+1;k<=n+1;++k)
a[j][k]-=a[i][k]*a[j][i]/a[i][i];
}
for(int i=n;i>=1;--i){
for(int j=i+1;j<=n;++j)
a[i][n+1]-=X[j]*a[i][j];
if(fabs(a[i][i])<1e-8) flag=0;
X[i]=a[i][n+1]/a[i][i];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n+1;++j)
scanf("%lf",&a[i][j]);
Guass();
if(!flag) return puts("No Solution"),0;
for(int i=1;i<=n;++i)
printf("%.2lf\n",X[i]);
return 0;
}
source:「洛谷 5491」二次剩余
#include
using namespace std;
typedef pair<int,int> pii;
int n,P;
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
namespace Cipolla{
int I,val;
struct num{
int x,y;
num(int x=0,int y=0):x(x),y(y){}
friend num operator*(const num &a,const num &b){
return num(add(mul(a.x,b.x),mul(val,mul(a.y,b.y))),add(mul(a.y,b.x),mul(a.x,b.y)));
}
friend num operator^(num a,int b){
num ans(1,0);
for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a;
return ans;
}
};
pii Sqrt(int n){
if(P==2) return pii(n,n);
if(!n) return pii(0,0);
if(power(n,(P-1)>>1)==P-1) return pii(-1,-1);
while(1){
I=rand()%P,val=dec(mul(I,I),n);
if(power(val,(P-1)>>1)==P-1) break;
}
int x=(num(I,1)^((P+1)>>1)).x;
return x=(x<P-x)?x:P-x,pii(x,P-x);
}
}
using namespace Cipolla;
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&P),n%=P;
pii ans=Sqrt(n);
if(ans.first==-1) puts("Hola!");
else if(ans.first==ans.second) printf("%d\n",ans.first);
else printf("%d %d\n",ans.first,ans.second);
}
return 0;
}