一.基础算法
1.高精度
2.三分
dl f(dl x){
}
dl sf(dl l,dl r){
dl mid,midr,ans;
while (fabs(r-l)>eps) {
mid=(l+r)/2;
midr=(mid+r)/2;
if(f(mid) > f(midr)) l=mid; else r=midr; //求最小值
}
ans=f(l);
zz=l;
return ans;
}
3.二分
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
二.数据结构
1.字典树
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N = 1e5+20;
int dx[]={1,-1,0,0,1,-1,1,-1};
int dy[]={0,0,1,-1,1,-1,-1,1};
queue<pii> q;
int n,m;
int a[N];
int tr[N*32][2],tot=1,res;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;
n=q*w; return n;
}
void insert(int x)
{
int p=1;
for(int i=30;~i;i--)
{
int &s=tr[p][x>>i&1];
if(!s) s=++idx;
p=s;
}
}
int search(int x)
{
int p=1,ans=0;
for(int i=30;~i;i--)
{
int s=x>>i&1;
if(tr[p][!s])
{
ans+=(1<<i);
p=tr[p][!s];
}
else p=tr[p][s];
}
return ans;
}
int main()
{
int n;
read(n);
for(int i=1;i<=n;i++) read(a[i]),insert(a[i]);
for(int i=1;i<=n;i++)
{
res=max(res,search(a[i]));
}
printf("%d\n",ans);
}
2.线段树
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
int w[N];
int n,m;
struct node {
int l,r;
ll sum,add;
}tr[N*4];
void pushup(int u)
{
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void pushdown(int u)
{
auto &root = tr[u]; auto &left = tr[u<<1]; auto &right = tr[u<<1|1];
if(root.add)
{
left.add+=root.add; left.sum+=(ll)(left.r-left.l+1)*root.add;
right.add+=root.add; right.sum+=(ll)(right.r-right.l+1)*root.add;
root.add=0;
}
}
void build(int u,int l,int r)
{
if(l==r) tr[u]={l,r,w[r],0};
else
{ tr[u].l=l;tr[u].r=r;
int mid = l+r>>1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int d)
{
if(tr[u].l>=l&&tr[u].r<=r)
{
tr[u].sum+=(ll)(tr[u].r-tr[u].l+1)*d;
tr[u].add+=d;
}
else
{
pushdown(u);
int mid = tr[u].l+tr[u].r>>1;
if(l<=mid) modify( u<<1, l, r, d);
if(r>mid) modify( u<<1|1,l, r, d);
pushup(u);
}
}
ll query(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid=tr[u].l + tr[u].r>>1;
ll sum=0;
if(mid>=l) sum=query(u<<1,l,r);
if(r>mid) sum+=query(u<<1|1,l,r);
return sum;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i];
build(1,1,n);
while(m--)
{
char op[2];
int l,r;
scanf("%s%d%d",op,&l,&r);
if(*op=='C')
{
int d;
cin>>d;
modify(1,l,r,d);
}
else
{
cout<<query(1,l,r)<<endl;
}
}
return 0;
}
3.Splay
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+10;
int n,m;
const int inf=0x3f3f3f3f;
struct node{
int s[2],p,v;
int rev,same;//rev反转,相同
int size,sum,ms,ls,rs;
void init(int _v,int _p)
{
s[0]=s[1]=0;
p=_p,v=_v;
rev=same=0;
size=1;
sum=ms=v;
ls=rs=max(v,0);
}
}tr[N];
int root,nodes[N],tt;
int w[N];
void pushup(int x)
{
auto &u = tr[x], &l = tr[u.s[0]], &r = tr[u.s[1]];
u.size = l.size + r.size + 1;
u.sum = l.sum + r.sum + u.v;
u.ls = max(l.ls, l.sum + u.v + r.ls);
u.rs = max(r.rs, r.sum + u.v + l.rs);
u.ms = max(max(l.ms, r.ms), l.rs + u.v + r.ls);
}
void pushdown(int x)
{
auto &u = tr[x], &l = tr[u.s[0]], &r = tr[u.s[1]];
if (u.same)
{
u.same = u.rev = 0;
if (u.s[0]) l.same = 1, l.v = u.v, l.sum = l.v * l.size;
if (u.s[1]) r.same = 1, r.v = u.v, r.sum = r.v * r.size;
if (u.v > 0)
{
if (u.s[0]) l.ms = l.ls = l.rs = l.sum;
if (u.s[1]) r.ms = r.ls = r.rs = r.sum;
}
else
{
if (u.s[0]) l.ms = l.v, l.ls = l.rs = 0;
if (u.s[1]) r.ms = r.v, r.ls = r.rs = 0;
}
}
else if (u.rev)
{
u.rev = 0, l.rev ^= 1, r.rev ^= 1;
swap(l.ls, l.rs), swap(r.ls, r.rs);
swap(l.s[0], l.s[1]), swap(r.s[0], r.s[1]);
}
}
void rotate(int x)
{
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x; // k=0表示x是y的左儿子;k=1表示x是y的右儿子
tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int x, int k)
{
while (tr[x].p != k)
{
int y = tr[x].p, z = tr[y].p;
if (z != k)
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
else rotate(y);
rotate(x);
}
if (!k) root = x;
}
void insert(int v)
{
int u = root, p = 0;
while (u) p = u, u = tr[u].s[v > tr[u].v];
u = ++ idx;
if (p) tr[p].s[v > tr[p].v] = u;
tr[u].init(v, p);
splay(u, 0);
}
int get_k(int k)
{
int u = root;
while (true)
{
pushdown(u);
if (tr[tr[u].s[0]].size >= k) u = tr[u].s[0];
else if (tr[tr[u].s[0]].size + 1 == k) return u;
else k -= tr[tr[u].s[0]].size + 1, u = tr[u].s[1];
}
//return -1;
}
int build(int l,int r,int p)
{
int mid=l+r>>1;
int u=nodes[tt--];
tr[u].init(w[mid],p);
if(l<mid) tr[u].s[0]=build(l,mid-1,u);
if(r>mid) tr[u].s[1]=build(mid+1,r,u);
pushup(u);
return u;
}
void dfs(int u)
{
if(tr[u].s[0]) dfs(tr[u].s[0]);
if(tr[u].s[1]) dfs(tr[u].s[1]);
nodes[++tt]=u;
}
int main()
{
for(int i=1;i<N;i++) nodes[++tt]=i;
scanf("%d%d",&n,&m);
tr[0].ms=w[n+1]=w[0]=-inf;
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
root=build(0,n+1,0);
char op[20];
while(m--)
{
scanf("%s",op);
if(!strcmp(op,"INSERT"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
for(int i=0;i<tot;i++) scanf("%d",&w[i]);
int l=get_k(posi+1),r=get_k(posi+2);
splay(l,0);
splay(r,l);
int u=build(0,tot-1,r);
tr[r].s[0]=u;
pushup(r),pushup(l);
}
else if(!strcmp(op,"DELETE"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
int l=get_k(posi),r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
dfs(tr[r].s[0]);
tr[r].s[0]=0;
pushup(r),pushup(l);
}
else if(!strcmp(op,"MAKE-SAME"))
{
int posi,tot,c;
scanf("%d%d%d",&posi,&tot,&c);
int l=get_k(posi),r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
auto &son=tr[tr[r].s[0]];
son.same=1,son.v=c,son.sum=son.size*c;
if(c>0) son.ms=son.ls=son.rs=son.sum;
else son.ms=c, son.ls=son.rs=0;
pushup(r), pushup(l);
}
else if(!strcmp(op,"REVERSE"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
int l=get_k(posi),r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
auto &son=tr[tr[r].s[0]];
son.rev^=1;
swap(son.ls,son.rs);
swap(son.s[0],son.s[1]);
pushup(r),pushup(l);
}
else if(!strcmp(op,"GET-SUM"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
int l=get_k(posi),r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
printf("%d\n",tr[tr[r].s[0]].sum);
}
else printf("%d\n",tr[root].ms);
}
}
4.树套树
线段树套平衡树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N = 5e4+10;
int a[N];
int L[N],R[N],T[N],idx;
int n,m;
struct node{
int s[2],p,v;
int size;
void init(int _v,int _p)
{
v=_v,p=_p;
size=1;
}
}tr[N*40];
void pushup(int u)
{
tr[u].size=tr[tr[u].s[0]].size+tr[tr[u].s[1]].size+1;
}
void rotate(int x)
{
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x; // k=0表示x是y的左儿子;k=1表示x是y的右儿子
tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int &root,int x, int k)
{
while (tr[x].p != k)
{
int y = tr[x].p, z = tr[y].p;
if (z != k)
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
else rotate(y);
rotate(x);
}
if (!k) root = x;
}
void insert(int &root ,int v)
{
int u = root, p = 0;
while (u) p = u, u = tr[u].s[v > tr[u].v];
u = ++ idx;
if (p) tr[p].s[v > tr[p].v] = u;
tr[u].init(v, p);
splay(root,u, 0);
}
void build(int u,int l,int r)
{
L[u]=l;R[u]=r;
insert(T[u],-inf),insert(T[u],inf);
for(int i=l;i<=r;i++)
{
insert(T[u],a[i]);
}
if(l==r) return ;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
int update(int &root,int x,int y)
{
int u=root;
while(u)
{
if(tr[u].v==x) break;
if(tr[u].v<x) u=tr[u].s[1];
else u=tr[u].s[0];
}
splay(root,u,0);
int l=tr[u].s[0],r=tr[u].s[1];
splay(root,u,0);
while(tr[l].s[1]) l=tr[u].s[1];
while(tr[r].s[0]) r=tr[u].s[0];
splay(root,l,0);
splay(root,r,l);
tr[l].s[0]=0;
pushup(r),pushup(l);
insert(root,y);
}
int get_k(int root,int v)
{
int u = root,res=0;
while (u)
{
if (tr[u].v<v) res+=tr[tr[u].s[0]].size+1,u=tr[u].s[1];
else u=tr[u].s[0];
}
return res;
}
int get_pre(int root,int x)
{
int u=root,res=-inf;
while(u)
{
if(tr[u].v<x) res=max(res,tr[u].v),u=tr[u].s[1];
else u=tr[u].s[0];
}
return res;
}
int get_suf(int root,int x)
{
int u=root,res=inf;
while(u)
{
if(tr[u].v>x) res=min(res,tr[u].v),u=tr[u].s[0];
else u=tr[u].s[1];
}
return res;
}
int query(int u,int l,int r,int x)
{
if(l<=L[u]&&R[u]<=r) return get_k(T[u],x)-1;
else {
int mid=L[u]+R[u]>>1;
int res=0;
if(l<=mid) res+=query(u<<1,l,r,x);
if(r>mid) res+=query(u<<1|1,l,r,x);
return res;
}
}
void change(int u,int pos,int x)
{
update(T[u],a[pos],x);
if(L[u]==R[u]) return;
int mid=L[u]+R[u]>>1;
if(pos<=mid) change(u<<1,pos,x);
if(pos>mid) change(u<<1|1,pos,x);
}
int query_pre(int u,int l,int r,int x)
{
if(l<=L[u]&&R[u]<=r) return get_pre(T[u],x);
int mid=L[u]+R[u]>>1,res=-inf;
if(l<=mid) res=max(res,query(u<<1,l,r,x));
if(r>mid) res=max(res,query(u<<1|1,l,r,x));
}
int query_suf(int u,int l,int r,int x)
{
if(l<=L[u]&&R[u]<=r) return get_suf(T[u],x);
int mid=L[u]+R[u]>>1,res=inf;
if(l<=mid) res=min(res,query_suf(u<<1,l,r,x));
if(r>mid) res=min(res,query_suf(u<<1|1,l,r,x));
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(m--)
{
int op;
cin>>op;
if(op==1)
{
int l,r,x;
cin>>l>>r>>x;
cout<<query(1,l,r,x)+1<<endl;
}
else if(op==2)
{
int l,r,x;
cin>>l>>r>>x;
int a=0,b=1e8;
while(l<r)
{
int mid=a+b+1>>1;
if(query(1,l,r,mid)+1<=x) a=mid;
else b=mid-1;
}
cout<<b<<endl;
}
else if(op==3)
{
int pos,x;
cin>>pos>>x;
change(1,pos,x);
a[pos]=x;
}
else if(op==4)
{
int l,r,x;
cin>>l>>r>>x;
cout<<query_pre(1,l,r,x);
}
else {
int l,r,x;
cin>>l>>r>>x;
cout<<query_suf(1,l,r,x);
}
}
}
5.主席树
区间k-th
#include<bits/stdc++.h>
using namespace std;
vector<int> ve;
int getid(int x){return lower_bound(ve.begin(),ve.end(),x)-ve.begin()+1;}
const int N=1e5+10;
int a[N];
struct node{
int l,r;
int sum;
}tr[N*40];
int cnt, root[N];
void insert(int l,int r,int pre,int &now,int x)
{
tr[++cnt]=tr[pre];
now=cnt;
tr[now].sum++;
if(l==r) return ;
int mid=(l+r)>>1;
if(x<=mid) insert(l,mid,tr[pre].l,tr[now].l,x);
else insert(mid+1,r,tr[pre].r,tr[now].r,x);
}
int query(int l,int r,int L,int R,int k)
{
if(l==r) return l;
int mid=l+r>>1;
int temp=tr[tr[R].l].sum-tr[tr[L].l].sum;
if(k<=temp)
{
query(l,mid,tr[L].l,tr[R].l,k);
}
else query(mid+1,r,tr[L].r,tr[R].r,k-temp);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],ve.push_back(a[i]);
sort(ve.begin(),ve.end());
ve.erase(unique(ve.begin(),ve.end()),ve.end());
for(int i=1;i<=n;i++)
{
insert(1,n,root[i-1],root[i],getid(a[i]));
}
while(m--)
{
int l,r,k;
cin>>l>>r>>k;
cout<<ve[query(1,n,root[l-1],root[r],k)-1]<<endl;
}
}
区间前k大和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int rt[N],idx;
ll pre[N];
int a[N];
vector<int> num;
struct node{
int l,r;
int cnt;
ll sum;
ll d;
}tr[N*40];
int n;
void init(){
pre[1]=1;
for(ll i=2;i<=100005;i++)
pre[i]=pre[i-1]+i*i;
}
void build(int &p,int l,int r){
p=++idx;
if(l==r){
return ;
}
int mid=l+r>>1;
build(tr[p].l,l,mid);
build(tr[p].r,mid+1,r);
}
int find(int x){
return lower_bound(num.begin(),num.end(),x)-num.begin()+1;
}
void insert(int &p,int q,int l,int r,int pos,int cnt){
p=++idx,tr[p]=tr[q];
tr[p].cnt+=1,tr[p].sum+=cnt;
if(l==r){
tr[p].d=cnt;
return ;
}
int mid=l+r>>1;
if(pos<=mid)
insert(tr[p].l,tr[q].l,l,mid,pos,cnt);
else
insert(tr[p].r,tr[q].r,mid+1,r,pos,cnt);
}
ll query(int p,int q,int l,int r,int k){
if(l==r){
return tr[p].d*k;
}
int cnt=tr[tr[p].r].cnt-tr[tr[q].r].cnt;
int mid=l+r>>1;
if(k<=cnt){
return query(tr[p].r,tr[q].r,mid+1,r,k);
}
else{
return tr[tr[p].r].sum-tr[tr[q].r].sum+query(tr[p].l,tr[q].l,l,mid,k-cnt);
}
}
int main(){
//ios::sync_with_stdio(false);
int t;
cin>>t;
init();
while(t--){
cin>>n;
int i;
idx=0;
num.clear();
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
num.push_back(a[i]);
rt[i]=0;
}
build(rt[0],1,n);
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
for(i=1;i<=n;i++){
insert(rt[i],rt[i-1],1,n,find(a[i]),a[i]);
}
int l,r,k;
int q;
cin>>q;
while(q--){
scanf("%d%d%d",&l,&r,&k);
printf("%lld\n",pre[(r-l+1)]+query(rt[r],rt[l-1],1,n,k));
}
}
}
6.树状数组
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
ll a[N],minv[N],maxv[N],tr[N<<1],n;
ll lowbit(int x)
{
return x&-x;
}
void modify(int x,int c)//修改树状数组x位置的值
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
ll query(int x)//查询区间1~x的区间和;
{
ll res=0;
for(int i=x;i>=1;i-=lowbit(i)) res+=tr[i];
return res;
}
7.笛卡尔树
#include<bits/stdc++.h>
#define ll long long
#define dl double
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
#define println(x){printf(":\n");for (int tmpi=1;tmpi<=n;tmpi++)printf("%d ",x[i]);printf("\n");}
using namespace std;
const int N=1e5+7;
const int INF=0x3f3f3f3f;
const int M=1e9+7;
struct Node {
int index, val; // index表示原数组的索引,val为当前节点的键值
Node *parent, *lefts, *rights; // 这三个指针分别指向父节点,左子节点,右子节点
Node(int id = 0, int v = 0, Node *l = NULL, Node *r = NULL) { index = id; val = v; lefts = l; rights = r; }
};
Node * build(int arr[], int fa[], int size) { // 这里构建一个根节点为最小值的笛卡尔树
std::stack<Node * > S; // 存储最右边路径的栈
Node *now, *next, *last;
for (int i = 1; i <= size; i++) {
next = new Node(i, arr[i]); last = NULL; // last用来指向最后被弹出栈的元素(若有弹出),它的作用后面会写到
while (!S.empty()) {
if (S.top()->val < next->val) { // 若栈顶节点的键值比当前节点键值小了,那么当前节点就做栈顶节点的右子节点
now = S.top();
if (now->rights) { // 而栈顶节点的原右子节点要变成当前节点的左子节点(由于前面一定与当前节点比较过了,栈顶节点右子树的键值一定都比当前节点大)
now->rights->parent = next;
next->lefts = now->rights;
fa[now->rights->index]=next->index;
}
now->rights = next;
next->parent = now;
fa[next->index]=now->index;
break;
}
last = S.top();
S.pop();
}
if (S.empty() && last) { // 这里为了特判一种可能出现的情况,就是当前节点把栈全部弹空了,就要把原先的根节点作为当前节点的左子节点
next->lefts = last;
last->parent = next;
fa[last->index]=next->index;
}
S.push(next);
}
while (!S.empty()) now = S.top(), S.pop();
return now;
};
//void dfs(Node* root){
// fa[root->lefts->index]=root->index;
// fa[root->rights->index]=root->index;
// lc[root->index]=root->lefts->index;
// rc[root->index]=root->rights->index;
// if (root->lefts) dfs(root->lefts);
// if (root->rights) dfs(root->rights);
// return;
//}
int n;
int a[N];
int fa[N];
void solve(){
read(n);
fo(i,1,n) read(a[i]);
// dfs(build(a,n));
build(a,fa,n);
fo(i,1,n) printf("%d ",fa[i]);
}
int main(){
// int T;
// scanf("%d",&T);
// for (int i=1;i<=T;i++)
solve();
return 0;
}
8.dsu on tree
#include<bits/stdc++.h>
#define fp(i,a,b) for(int i=a;i<=b;i++)
typedef long long ll;
typedef double dl;
using namespace std;
const int N=2e5+7;
const int M=2*N;
const int INF=0x3f3f3f3f;
int e[M],w[M],ne[M],h[N],idx;
ll sz[N],top[N],fa[N],son[N],ans[N],col[N],cnt[N];
ll n,m,Son=0,mx=0,sum=0;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void add(int u,int fa,int val)
{
cnt[col[u]]+=val;
if(cnt[col[u]]>mx) sum=col[u],mx=cnt[col[u]];
else if(cnt[col[u]]==mx)
{
sum+=col[u];
}
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa||j==Son) continue;
add(j,u,val);
}
}
void dfs1(int u,int father)
{
sz[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==father) continue;
dfs1(j,u);
sz[u]+=sz[j];
if(sz[son[u]]<sz[j]) son[u]=j;
}
}
void dfs2(int u,int fa,int op)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa||j==son[u]) continue;
dfs2(j,u,0);
}
if(son[u]) dfs2(son[u],u,1) ,Son=son[u];
add(u,fa,1);
Son=0;
ans[u]=sum;
if(!op) add(u,fa,-1) ,sum=0,mx=0;
}
void solve()
{
memset(h,-1,sizeof(h));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&col[i]);
m=n-1;
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++)
{
printf("%lld ",ans[i]);
}
}
三.字符串
1.KMP
1.KMP
#include <iostream>
using namespace std;
const int N = 10010, M = 100010;
int n, m;
int ne[N];
char s[M], p[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
for (int i = 2, j = 0; i <= n; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == n)
{
printf("%d ", i - n);
j = ne[j];
}
}
return 0;
}
2.EXKMP
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
typedef long long ll;
typedef double dl;
using namespace std;
const int N=2e6+7;
const int M=1e9+7;
const int INF=0x3f3f3f3f;
char S[N], T[N];
int slen, tlen;
int nxt[N];
int ext[N];
void get_next()
{
int i = 0, j, po;
nxt[0] = tlen;
while (T[i] == T[i + 1] && i + 1 < tlen)
i++;
nxt[1] = i;
po = 1;
for (i = 2; i < tlen; i++)
{
if (nxt[i - po] + i < nxt[po] + po)
nxt[i] = nxt[i - po];
else
{
j = nxt[po] + po - i;
if (j < 0)j = 0;
while (i + j < tlen && T[j] == T[j + i])
j++;
nxt[i] = j;
po = i;
}
}
}
void EXKMP()
{
int i = 0, j, po, slen = strlen(S), tlen = strlen(T);
get_next();//计算子串的nxt数组
while(S[i] == T[i] && i < tlen && i < slen) //计算ext[0]
i++;
ext[0] = i;
po = 0; //初始化po的位置
for(i = 1; i < slen; i++)
{
if(nxt[i - po] + i < ext[po] + po) //第一种情况,直接可以得到ext[i]的值
ext[i] = nxt[i - po];
else//第二种情况,要继续匹配才能得到ext[i]的值
{
j = ext[po] + po - i;
if(j < 0)j = 0; //如果i>ext[po]+po则要从头开始匹配
while(i + j < slen && j < tlen && S[j + i] == T[j]) //计算ext[i]
j++;
ext[i] = j;
po = i; //更新po的位置
}
}
}
//#include<local.h>
//using namespace Debug;
void solve()
{
cin >> S >> T;
slen = strlen(S);
tlen = strlen(T);
EXKMP();
// println(nxt,nxt+tlen,"nxt");
// println(ext+0,ext+slen,"ext");
ll ans = 0;
fo(i, 0, slen - 1){
if (S[i + ext[i]] < T[ext[i]]) ans+=slen-i-ext[i];
ans+=min(tlen-1,ext[i]);
}
cout << ans;
}
int main(){
// int T;read(T);
// for(int i=1;i<=T;i++)
solve();
}
2.AC自动机
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 6;
int n;
namespace AC {
int tr[N][26], tot;
int e[N], fail[N];
void insert(char *s) {
int u = 0;
for (int i = 1; s[i]; i++) {
if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++tot;
u = tr[u][s[i] - 'a'];
}
e[u]++;
}
queue<int> q;
void build() {
for (int i = 0; i < 26; i++)
if (tr[0][i]) q.push(tr[0][i]);
while (q.size()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
if (tr[u][i])
fail[tr[u][i]] = tr[fail[u]][i], q.push(tr[u][i]);
else
tr[u][i] = tr[fail[u]][i];
}
}
}
int query(char *t) {
int u = 0, res = 0;
for (int i = 1; t[i]; i++) {
u = tr[u][t[i] - 'a']; // 转移
for (int j = u; j && e[j] != -1; j = fail[j]) {
res += e[j], e[j] = -1;
}
}
return res;
}
} // namespace AC
char s[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%s", s + 1), AC::insert(s);
scanf("%s", s + 1);
AC::build();
printf("%d", AC::query(s));
return 0;
}
3.马拉车
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5;
int len1,len2,p[N],ans;
char str[N],s[N];
void init()
{
str[0]='$';//构建新的字符串,将无论奇偶都形成奇数长度的字符串 ;
str[1]='#';
for(int i=0;i<len1;i++)
{
str[i*2+2]=s[i];
str[i*2+3]='#';
}
len2=len1*2+2;
str[len2]='*';
}
void manacher()
{
int id=0,mx=0;
for(int i=1;i<len2;i++)
{
if(mx>i) p[i]=min(p[2*id-i],mx-i);
else p[i]=1;
for(;str[i+p[i]]==str[i-p[i]];p[i]++); //扩展长度;
if(p[i]+i>mx)
{
mx=p[i]+i;
id=i;
}
}
}
int main()
{
while(scanf("%s",s)!=EOF)
{
len1=strlen(s);
init();
manacher();
ans=0;
for(int i=0;i<len2;i++)
{
ans=max(p[i],ans);
}
cout<<ans-1<<endl;
}
}
4.哈希
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
const int N = 100010, P = 131;
int n, m;
char str[N];
ULL h[N], p[N];
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
scanf("%d%d", &n, &m);
scanf("%s", str + 1);
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
while (m -- )
{
int l1, r1, l2, r2;
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if (get(l1, r1) == get(l2, r2)) puts("Yes");
else puts("No");
}
return 0;
}
5.双哈希
struct Hash
{
ull h1[maxn],h2[maxn],p1[maxn],p2[maxn],P1 = 13331,P2 = 131,mod1 = 1e9+7,mod2 = 1e8+7;
char s[maxn];int len;
void init_pow(){
p1[0] = 1,p2[0] = 1;
h1[0] = 0,h2[0] = 0;
for(int i = 1;i<=maxn;i++){
p1[i] = p1[i-1]*P1%mod1;
p2[i] = p2[i-1]*P2%mod2;
}
}
void init_hs(){
len = strlen(s+1);
for(int i = 1;i<=len;i++){
h1[i] = (h1[i-1]*P1%mod1+(s[i]-'a'))%mod1;
h2[i] = (h2[i-1]*P2%mod2+(s[i]-'a'))%mod2;
}
}
pii geths(int l,int r){
ull hs1 = (h1[r] - h1[l-1]*p1[r-l+1]%mod1 + mod1)%mod1;
ull hs2 = (h2[r] - h2[l-1]*p2[r-l+1]%mod2 + mod2)%mod2;
return {hs1,hs2};
}
}H;
6.序列自动机
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+20;
int dp[N][30];
char a[N],b[N];
int n,m;
void build()
{
memset(dp,-1,sizeof(dp));
for(int j=n-1;j>=0;j--)
{
for(int i=0;i<26;i++)
{
if(a[j+1]-'a'==i)
dp[j][i]=j+1;
else dp[j][i]=dp[j+1][i];
}
}
}
int main()
{
cin>>n>>m;
cin>>a+1;
build();
while(m--)
{
cin>>b;
bool flag=0;
int j=0;
int len=strlen(b);
for(int i=0;i<len;i++)
{
if(dp[j][b[i]-'a']==-1)
{
flag=1;
break;
}
j=dp[j][b[i]-'a'];
}
if(flag==1)
cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
}
7.SAM
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
typedef long long ll;
typedef double dl;
using namespace std;
const int maxp=26,maxn=2e5+7;
const int M=1e9+7;
const int INF=0x3f3f3f3f;
int n,m;
struct Suffix_Node{
int trans[maxp],par,len;
void Clear(){
memset(trans,0,sizeof(trans));
par=len=0;
}
};
class Suffix_Automaton{
private:
Suffix_Node S[maxn];
int p,np,size,crp;
public:
Suffix_Automaton():p(1),np(1),size(1),crp(1){};
const void Clear(){
for(int i=0;i<=size;++i) S[i].Clear();
p=np=size=crp=1;
}
const int Match(const char ch)const{
return S[crp].trans[ch-'a'];
}
const void Withdraw(const int len){
while( crp!=0 && S[S[crp].par].len>=len ) crp=S[crp].par;
if( crp==0 ) crp=1;
}
const void Transfer(const int len,const char ch){
crp=S[crp].trans[ch-'a'];
if( crp==0 ) crp=1;
Withdraw(len);
}
const void Insert(const char ch){
int x=ch-'a';
np=++size;
S[np].len=S[p].len+1;
while( p!=0 && !S[p].trans[x] ) S[p].trans[x]=np,p=S[p].par;
if( p==0 ) S[np].par=1;
else{
int q,nq;
q=S[p].trans[x];
if( S[q].len==S[p].len+1 ) S[np].par=q;
else{
nq=++size;
S[nq]=S[q],S[nq].len=S[p].len+1;
S[np].par=S[q].par=nq;
while( p!=0 && S[p].trans[x]==q ) S[p].trans[x]=nq,p=S[p].par;
}
}
p=np;
}
}SAM;
void solve(){
}
int main(){
// int T;read(T);
// for(int i=1;i<=T;i++)
solve();
}
8.回文树
struct PAM{ // 每个节点代表一个回文串
int next[maxn][ALP]; // next指针,参照Trie树
int fail[maxn]; // fail失配后缀链接
int cnt[maxn]; // 此回文串出现个数
int num[maxn];
int len[maxn]; // 回文串长度
int s[maxn]; // 存放添加的字符
int last; //指向上一个字符所在的节点,方便下一次add
int n; // 已添加字符个数
int p; // 节点个数
int newnode(int w){ // 初始化节点,w=长度
for(int i=0;i<ALP;i++)
next[p][i] = 0;
cnt[p] = 0;
num[p] = 0;
len[p] = w;
return p++;
}
void init(){
p = 0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
s[n] = -1; // 开头放一个字符集中没有的字符,减少特判
fail[0] = 1;
}
int get_fail(int x){ // 和KMP一样,失配后找一个尽量最长的
while(s[n-len[x]-1] != s[n]) x = fail[x];
return x;
}
void add(int c){
c -= 'a';
s[++n] = c;
int cur = get_fail(last);
if(!next[cur][c]){
int now = newnode(len[cur]+2);
fail[now] = next[get_fail(fail[cur])][c];
next[cur][c] = now;
num[now] = num[fail[now]] + 1;
}
last = next[cur][c];
cnt[last]++;
}
void count(){
// 最后统计一遍每个节点出现个数
// 父亲累加儿子的cnt,类似SAM中parent树
// 满足parent拓扑关系
for(int i=p-1;i>=0;i--)
cnt[fail[i]] += cnt[i];
}
}pam;
9.莫队
四.图论
1.最短路
1.优化dijkstra
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1});
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
cout << dijkstra() << endl;
return 0;
}
2.spfa
#include<bits/stdc++.h>
using namespace std;
const int N = 5210;
int w[N],ne[N],e[N],h[N];
int idx=0;
int cnt[N];
bool st[N];
int dist[N];
int n;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool spfa()
{
memset(dist, 0, sizeof dist);
memset(cnt, 0, sizeof cnt);
memset(st, 0, sizeof st);
queue <int> q;
for(int i=1;i<=n;i++)
{
q.push(i);
st[i]=1;
}
while(!q.empty())
{
int t=q.front();
q.pop();
st[t]=0;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) return 1;
if(!st[j])
{
q.push(j);
st[j]=1;
}
}
}
}
return 0;
}
int main()
{
int T;
scanf("%d",&T);
//std::ios::sync_with_stdio(false);
while(T--)
{
int m1,m2;
idx=0;
memset(h,-1,sizeof(h));
scanf("%d%d%d",&n,&m1,&m2);
while(m1--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
while(m2--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
if(spfa()) puts("YES");
else puts("NO");
}
return 0;
}
3.第K短路
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int N = 1010, M = 200010;
int n, m, S, T, K;
int h[N], rh[N], e[M], w[M], ne[M], idx;
int dist[N], cnt[N];
bool st[N];
void add(int h[], int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dijkstra()
{
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, T});
memset(dist, 0x3f, sizeof dist);
dist[T] = 0;
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.y;
if (st[ver]) continue;
st[ver] = true;
for (int i = rh[ver]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
}
int astar()
{
priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
heap.push({dist[S], {0, S}});
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.y.y, distance = t.y.x;
cnt[ver] ++ ;
if (cnt[T] == K) return distance;
for (int i = h[ver]; ~i; i = ne[i])
{
int j = e[i];
if (cnt[j] < K)
heap.push({distance + w[i] + dist[j], {distance + w[i], j}});
}
}
return -1;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh);
for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(h, a, b, c);
add(rh, b, a, c);
}
scanf("%d%d%d", &S, &T, &K);
if (S == T) K ++ ;
dijkstra();
printf("%d\n", astar());
return 0;
}
4.最短路计数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 400010, mod = 100003;
int n, m;
int h[N], e[M], ne[M], idx;
int dist[N], cnt[N];
int q[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void bfs()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
cnt[1] = 1;
int hh = 0, tt = 0;
q[0] = 1;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + 1)
{
dist[j] = dist[t] + 1;
cnt[j] = cnt[t];
q[ ++ tt] = j;
}
else if (dist[j] == dist[t] + 1)
{
cnt[j] = (cnt[j] + cnt[t]) % mod;
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
bfs();
for (int i = 1; i <= n; i ++ ) printf("%d\n", cnt[i]);
return 0;
}
2.SPFA求负环
#include<bits/stdc++.h>
using namespace std;
const int N = 5210;
int w[N],ne[N],e[N],h[N];
int idx=0;
int cnt[N];
bool st[N];
int dist[N];
int n;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool spfa()
{
memset(dist, 0, sizeof dist);
memset(cnt, 0, sizeof cnt);
memset(st, 0, sizeof st);
queue <int> q;
for(int i=1;i<=n;i++)
{
q.push(i);
st[i]=1;
}
while(!q.empty())
{
int t=q.front();
q.pop();
st[t]=0;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) return 1;
if(!st[j])
{
q.push(j);
st[j]=1;
}
}
}
}
return 0;
}
int main()
{
int T;
scanf("%d",&T);
//std::ios::sync_with_stdio(false);
while(T--)
{
int m1,m2;
idx=0;
memset(h,-1,sizeof(h));
scanf("%d%d%d",&n,&m1,&m2);
while(m1--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
while(m2--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
if(spfa()) puts("YES");
else puts("NO");
}
return 0;
}
3.差分约束
求一组如 x i ≤ y i + c x_i \leq y_i+c xi≤yi+c 的不等式组的可行解
从源点出发可以遍历到所有的边,才能满足所有条件
令差值最大
$x_i \leq y_i+c $
add(j,i,c);
求最短路,有负环无解
令差值最小
$x_i \geq y_i+c $
add(j,i,c);
求最长路,有正环无解
4.树链刨分
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+20;
const int M = 2*N;
int e[M],w[M],ne[M],h[N],idx;
int id[N],nw[N],cnt=0;
int dep[N],sz[N],top[N],fa[N],son[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
struct node{
int l,r;
ll add,sum;
}tr[N<<2];
void dfs1(int u, int father, int depth)
{
dep[u] = depth, fa[u] = father, sz[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == father) continue;
dfs1(j, u, depth + 1);
sz[u] += sz[j];
if (sz[son[u]] < sz[j]) son[u] = j;
}
}
void dfs2(int u, int t)
{
id[u] = ++ cnt, nw[cnt] = w[u], top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u)
{
auto &root =tr[u],&left =tr[u<<1],&right=tr[u<<1|1];
if(root.add)
{
left.add+=root.add,right.add+=root.add;
left.sum+=(left.r-left.l+1)*root.add;
right.sum+=(right.r-right.l+1)*root.add;
root.add=0;
}
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u]={l,r,0,nw[l]};
return ;
}
tr[u]={l,r,0};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int k)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].add+=k;
tr[u].sum+=(tr[u].r-tr[u].l+1)*k;
return ;
}
else
{
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) update(u<<1,l,r,k);
if(r>mid) update(u<<1|1,l,r,k);
pushup(u);
}
}
ll query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;
else {
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
ll res=0;
if(l<=mid) res+=query(u<<1,l,r);
if(r>mid) res+=query(u<<1|1,l,r);
return res;
}
}
void update_path(int u,int v,int k)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,id[top[u]],id[u],k);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
update(1,id[v],id[u],k);
}
ll query_path(int u,int v)
{
ll res=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res+=query(1,id[top[u]],id[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
res+=query(1,id[v],id[u]);
return res;
}
void update_tree(int u,int k)
{
update(1,id[u],id[u]+sz[u]-1,k);
}
ll query_tree(int u)
{
return query(1,id[u],id[u]+sz[u]-1);
}
int main()
{
int n,m;
scanf("%d",&n);
memset(h,-1,sizeof(h));
for(int i=1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
dfs1(1,-1,1);
dfs2(1,1);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int op,u,v,k;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&u,&v,&k);
update_path(u,v,k);
}
if(op==2)
{
scanf("%d%d",&u,&k);
update_tree(u,k);
}
if(op==3)
{
scanf("%d%d",&u,&v);
printf("%lld\n",query_path(u,v));
}
if(op==4)
{
scanf("%d",&u);
printf("%lld\n",query_tree(u));
}
}
}
5.倍增lca
#include<bits/stdc++.h>
using namespace std;
const int N = 4E4+10;
const int M = 2*N;
int h[N],ne[M],e[M],idx;
int depth[N];
int fa[N][30];
queue<int> q;
void add(int a, int b )
{
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
void bfs(int root)
{
memset(depth,0x3f,sizeof(depth));
depth[0]=0;depth[root]=1;
q.push(root);
while(q.size()!=0)
{
int t=q.front();
q.pop();
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(depth[j]>depth[t]+1)
{
depth[j]=depth[t]+1;
q.push(j);
fa[j][0]=t;
for(int k=1;k<=15;k++)
{
fa[j][k]=fa[fa[j][k-1]][k-1];
}
}
}
}
}
int lca(int a,int b)
{
if(depth[a]<depth[b]) swap(a,b);
for(int k=15;k>=0;k--)
{
if(depth[fa[a][k]]>=depth[b])
{
a=fa[a][k];
}
}
if(a==b) return a;
for(int k=15;k>=0;k--)
{
if(fa[a][k]!=fa[b][k])
{
a=fa[a][k];
b=fa[b][k];
}
}
return fa[a][0];
}
int main()
{
int t,m ;
memset(h,-1,sizeof(h));
cin>>t;
int root=0;
for(int i=1;i<=t;i++)
{
int a,b;
cin>>a>>b;
if(b==-1)
root=a;
else
{
add(a,b);
add(b,a);
}
}
bfs(root);
scanf("%d", &m);
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
int p = lca(a, b);
if (p == a) puts("1");
else if (p == b) puts("2");
else puts("0");
}
}
6.拓扑排序
#include<bits/stdc++.h>
using namespace std;
const int N=1E5+10;
const int M=1e5+10;
int ne[N],e[N],h[N],idx;
int d[N];
int n,m;
vector <int> ans;
void add(int a,int b)
{
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
void top_sort()
{
queue<int> q;
for(int i=1;i<=n;i++)
{
if(d[i]==0)
{
q.push(i);
}
}
while(q.size()!=0)
{
auto t=q.front();
q.pop();
ans.push_back(t);
for(int i=h[t];i!=-1;i=ne[i])
{
int j=ne[i];
d[j]--;
if(d[j]==0) q.push(j);
}
}
if(ans.size()==n)
{
for(auto x:ans) printf("%d ", x);
}
else cout<<"-1";
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof(h));
for(int i=0;i<m;i++)
{
int a,b;
cin>>a>>b;
add(a,b);
d[b]++;
}
top_sort();
}
7.最小生成树
1.kruskal
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
int f[maxn];
struct node {
int u;int v;int w;
}edges[maxn];
bool cmp(node a,node b){
return a.w<b.w;
}
int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
void join(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
f[x]=y;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=0;i<m;i++)
{
cin>>edges[i].u>>edges[i].v>>edges[i].w;
}
sort(edges,edges+m,cmp);
int res=0,cnt=0;
for(int i=0;i<m;i++)
{
int a=edges[i].u;int b=edges[i].v;int w=edges[i].w;
a=find(a),b=find(b);
if(a!=b)
{
join(a,b);
res+=w;
cnt++;
}
}
if(cnt < n-1) cout<<"impossible";
else cout<<res;
}
2.prime
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);
}
int t = prim();
if (t == INF) puts("impossible");
else printf("%d\n", t);
return 0;
}
3.次小生成树
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=510,M=10010;
int n,m,idx=0;
struct node {
int a,b,w;
bool f;
}edge[M];
int f[N];
int dist1[N][N],dist2[N][N];
int h[N], e[N * 2], w[N * 2], ne[N * 2];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
return f[x]!=x?f[x]=find(f[x]):x;
}
void join(int x,int y)
{
x=find(x); y = find(y);
if(x!=y)
f[x]=y;
}
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void dfs(int u,int fa,int maxd1,int maxd2,int d1[],int d2[])
{
d1[u]=maxd1,d2[u]=maxd2;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(j!=fa)
{
int td1=maxd1,td2=maxd2;
if(w[i]>td1) td2=td1 , td1=w[i];
else if(w[i]>td2) td2=w[i];
dfs(j,u,td1,td2,d1,d2);
}
}
}
int main(){
int n,m;
cin>>n>>m;
memset(h,-1,sizeof(h));
for(int i=0;i<m;i++)
{
cin>>edge[i].a>>edge[i].b>>edge[i].w;
}
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=n;i++)
find(i);
sort(edge,edge+m,cmp);
LL sum=0;
for(int i=0;i<m;i++)
{
int a=edge[i].a; int b=edge[i].b; int w=edge[i].w;
int pa=find(a), pb=find(b);
if(pa!=pb)
{
sum+=w;
join(edge[i].a,edge[i].b);
add(a,b,w); add(b,a,w);
edge[i].f=1;
}
}
for(int i=1;i<=n;i++)
dfs(i,-1,0,0,dist1[i],dist2[i]);
LL res=1e18;
for(int i=0;i<m;i++)
{
if(!edge[i].f)
{
int a=edge[i].a,b=edge[i].b,w=edge[i].w;
LL t;
if(w>dist1[a][b])
t=sum+w-dist1[a][b];
else if(w>dist2[a][b])
t=sum+w-dist2[a][b];
res=min(res,t);
}
}
cout<<res<<endl;
return 0;
}
8.网络流
1.dinic
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int INF=0x3f3f3f3f;
const int N=10001;
const int maxn=10001;
class edge{
public:
int to,flow,cap;
};
class network_flow{
public:
int n;
int s,t;
int vis[maxn];
int d[maxn];
int cur[maxn];
vector<int> G[maxn];
vector<edge> edges;
void init(int n){
this->n=n;
edges.clear();
for (int i=0;i<=n;i++){
G[i].clear();
}
}
void addedge(int from,int to,int cap){
edges.push_back((edge){to,0,cap});
G[from].push_back(edges.size()-1);
edges.push_back((edge){from,0,0});
G[to].push_back(edges.size()-1);
// printf("%d %d %d\n-",from,to,cap);
}
bool bfs(){
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(s);
d[s]=0;
vis[s]=1;
while(!q.empty()){
int x=q.front(); q.pop();
for (int i=0;i<G[x].size();i++){
edge& e=edges[G[x][i]];
if (vis[e.to]||e.cap<=e.flow) continue;
d[e.to]=d[x]+1;
vis[e.to]=1;
q.push(e.to);
}
}
return vis[t];
}
int dfs(int x,int a){
if (x==t||a==0) return a;
int flow=0,f;
for (int& i=cur[x];i<G[x].size();i++){
edge& e=edges[G[x][i]];
if (d[e.to]==d[x]+1){
f=dfs(e.to,min(a,e.cap-e.flow));
if (f>0){
flow+=f;
a-=f;
edges[G[x][i]].flow+=f;
edges[G[x][i]^1].flow-=f;
if (a==0) break;
}
}
}
return flow;
}
int maxflow(int s,int t,int &flow){
this->s=s; this->t=t;
int f;
while(bfs()){
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
}nf;
int n,m,s,t;
int u,v,c;
void solve(){
scanf("%d%d",&n,&m);
s=maxn-2,t=maxn-1;
nf.init(t);
int flow=0;
nf.maxflow(s,t,flow);
printf("%d\n",flow);
return;
}
int main(){
solve();
return 0;
}
2.费用流
#include<bits/stdc++.h>
#define maxn 10000
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N = 1000;
const int INF = 0x3f3f3f3f;
struct edge{
int from,to,flow,cap,cost;
};
class poi{
public:
vector<edge> edges;
vector<int> G[maxn];
int a[maxn];
int p[maxn];
int d[maxn];
bool inq[maxn];
int n,m,s,t;
void init(int n){
this->n=n;
for (int i=0;i<=n;i++) G[i].clear();
edges.clear();
}
void addedge(int from,int to,int cap,int cost){
edges.push_back((edge){from,to,0,cap,cost});
G[from].push_back(edges.size()-1);
edges.push_back((edge){to,from,0,0,-cost});
G[to].push_back(edges.size()-1);
}
bool spfa(int s,int t,int& flow,int& cost){
memset(d,0x3f,sizeof(d));
memset(inq,0,sizeof(inq));
queue<int> q;
q.push(s); d[s]=0; inq[s]=1; a[s]=INF; p[s]=0;
while(!q.empty()){
int x=q.front(); q.pop(); inq[x]=0;
for (int i=0;i<G[x].size();i++){
edge& e=edges[G[x][i]];
if (d[e.to]>d[e.from]+e.cost&&e.cap>e.flow){
d[e.to]=d[e.from]+e.cost;
a[e.to]=min(a[e.from],e.cap-e.flow);
p[e.to]=G[x][i];
if (!inq[e.to]){
inq[e.to]=1;
q.push(e.to);
}
}
}
}
if (d[t]==INF) return 0;
flow+=a[t];
cost+=a[t]*d[t];
int u=t;
while(u!=s){
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return 1;
}
int maxcost(int s,int t,int &flow,int &cost){
for(int i=0;i<edges.size();i+=2){
//如果先跑了一遍最小费用流,需要先清空初始化网络
edges[i].flow+=edges[i^1].flow;
edges[i^1].flow=0;
//边权取反求最长路
edges[i].cost=-edges[i].cost;
edges[i^1].cost=-edges[i^1].cost;
}
mincost(s,t,flow,cost);
cost=-cost;
return cost;
}
int mincost(int s,int t,int &flow,int &cost){
flow=0,cost=0;
while(spfa(s,t,flow,cost)){
}
return cost;
}
}mc;
int n,m,s,t;
void solve(){
scanf("%d%d",&n,&m);
s=0,t=maxn-1;
mc.init(t);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&c,&w);
mc.addedge(u,v,c,w);
}
int flow=0,cost=0;
mc.mincost(s,t,flow,cost);
printf("%d %d\n",flow,cost);
}
int main(){
solve();
return 0;
}
3.二分图染色
int flag;
vector<int> ve[N];
int col[N];
int n,m;
struct node{
int u,v,w;
}g[N];
bool cmp(node a,node b)
{
return a.w<b.w;
}
bool dfs(int u, int c) {
col[u] = c;
for (auto v : ve[u]) {
if (col[v] == c) return 0;
if (!col[v] && !dfs(v, -c)) return 0;
}
return 1;
}
bool ok(int x) {
for (int i = 1; i <= n; i++)
ve[i].clear(), col[i] = 0;
for (int i = 1; i <= m; i++) {
if (g[i].w > x) {
ve[g[i].u].push_back(g[i].v);
ve[g[i].v].push_back(g[i].u);
}
}
for (int i = 1; i <= n; i++)
if (!col[i]) {
if (!dfs(i, 1)) return 0;
}
return 1;
}
五.数学
1.数论
1.FFT
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
const double PI = acos(-1.0);
//复数结构体
struct complex
{
double r,i;
complex(double _r = 0.0,double _i = 0.0)
{
r = _r; i = _i;
}
complex operator +(const complex &b)
{
return complex(r+b.r,i+b.i);
}
complex operator -(const complex &b)
{
return complex(r-b.r,i-b.i);
}
complex operator *(const complex &b)
{
return complex(r*b.r-i*b.i,r*b.i+i*b.r);
}
};
/*
* 进行FFT和IFFT前的反转变换。
* 位置i和 (i二进制反转后位置)互换
* len必须去2的幂
*/
void change(complex y[],int len)
{
int i,j,k;
for(i = 1, j = len/2;i < len-1; i++)
{
if(i < j)swap(y[i],y[j]);
//交换互为小标反转的元素,i<j保证交换一次
//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
k = len/2;
while( j >= k)
{
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
/*
* 做FFT
* len必须为2^k形式,
* on==1时是DFT,on==-1时是IDFT
*/
void fft(complex y[],int len,int on)
{
change(y,len);
for(int h = 2; h <= len; h <<= 1)
{
complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
for(int j = 0;j < len;j+=h)
{
complex w(1,0);
for(int k = j;k < j+h/2;k++)
{
complex u = y[k];
complex t = w*y[k+h/2];
y[k] = u+t;
y[k+h/2] = u-t;
w = w*wn;
}
}
}
if(on == -1)
for(int i = 0;i < len;i++)
y[i].r /= len;
}
const int MAXN = 200010;
complex x1[MAXN],x2[MAXN];
char str1[MAXN/2],str2[MAXN/2];
int sum[MAXN];
int main()
{
while(scanf("%s%s",str1,str2)==2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
int len = 1;
while(len < len1*2 || len < len2*2)len<<=1;
for(int i = 0;i < len1;i++)
x1[i] = complex(str1[len1-1-i]-'0',0);
for(int i = len1;i < len;i++)
x1[i] = complex(0,0);
for(int i = 0;i < len2;i++)
x2[i] = complex(str2[len2-1-i]-'0',0);
for(int i = len2;i < len;i++)
x2[i] = complex(0,0);
//求DFT
fft(x1,len,1);
fft(x2,len,1);
for(int i = 0;i < len;i++)
x1[i] = x1[i]*x2[i];
fft(x1,len,-1);
for(int i = 0;i < len;i++)
sum[i] = (int)(x1[i].r+0.5);
for(int i = 0;i < len;i++)
{
sum[i+1]+=sum[i]/10;
sum[i]%=10;
}
len = len1+len2-1;
while(sum[len] <= 0 && len > 0)len--;
for(int i = len;i >= 0;i--)
printf("%c",sum[i]+'0');
printf("\n");
}
return 0;
}
2.快速幂
int qmi(int a, int k, int p){
int res = 1;
while(k){
if(k & 1) res = (LL) res * a % p;
k >>= 1;
a = (LL) a * a % p;
}
return res;
}
3.exgcd
//扩展欧几里得算法,计算a和b的最大公约数以及对应的系数
int exgcd(int a, int b, int &x, int &y){
if(!b){
y = 0, x = 1;
return a;//最大公约数为a
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
4.莫比乌斯反演
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
typedef long long ll;
typedef double dl;
using namespace std;
const int N=1e7+7;
const int M=1e9+7;
const int INF=0x3f3f3f3f;
int n,m;
int tot,pri[N],phi[N];
ll sum[N],ans;
bool mark[N];
void getphi(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!mark[i]){
phi[i]=i-1;
pri[++tot]=i;
}
for(int j=1;j<=tot;j++){
int x=pri[j];
if(i*x>n)break;
mark[i*x]=1;
if(i%x==0){
phi[i*x]=phi[i]*x;
break;
}
else
phi[i*x]=phi[i]*phi[x];
}
}
}
void solve(){
read(n);
getphi(n);
fo(i,1,n){
sum[i]=sum[i-1]+phi[i];
}
fo(i,1,tot){
ans+=sum[n/pri[i]];
}
ans<<=1;
ans-=tot;
cout<<ans<<endl;
}
int main(){
// int T;read(T);
// for(int i=1;i<=T;i++)
solve();
}
5.欧拉函数
int phi(int x)
{
int res = x;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
{
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
if (x > 1) res = res / x * (x - 1);
return res;
}
6.高斯消元解线性方程组
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 110;
const double eps = 1e-6;
int n;
double a[N][N];
int gauss()
{
int c, r;
for (c = 0, r = 0; c < n; c ++ )
{
int t = r;
for (int i = r; i < n; i ++ )
if (fabs(a[i][c]) > fabs(a[t][c]))
t = i;
if (fabs(a[t][c]) < eps) continue;
for (int i = c; i < n + 1; i ++ ) swap(a[t][i], a[r][i]);
for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c];
for (int i = r + 1; i < n; i ++ )
if (fabs(a[i][c]) > eps)
for (int j = n; j >= c; j -- )
a[i][j] -= a[r][j] * a[i][c];
r ++ ;
}
if (r < n)
{
for (int i = r; i < n; i ++ )
if (fabs(a[i][n]) > eps)
return 2;
return 1;
}
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
a[i][n] -= a[j][n] * a[i][j];
return 0;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n + 1; j ++ )
cin >> a[i][j];
int t = gauss();
if (t == 0)
{
for (int i = 0; i < n; i ++ ) printf("%.2lf\n", a[i][n]);
}
else if (t == 1) puts("Infinite group solutions");
else puts("No solution");
return 0;
}
7.矩阵乘法
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 3;
int n, m;
void mul(int c[], int a[], int b[][N])
{
int temp[N] = {0};
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
temp[i] = (temp[i] + (LL)a[j] * b[j][i]) % m;
memcpy(c, temp, sizeof temp);
}
void mul(int c[][N], int a[][N], int b[][N])
{
int temp[N][N] = {0};
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
for (int k = 0; k < N; k ++ )
temp[i][j] = (temp[i][j] + (LL)a[i][k] * b[k][j]) % m;
memcpy(c, temp, sizeof temp);
}
int main()
{
cin >> n >> m;
int f1[N] = {1, 1, 1};
int a[N][N] = {
{0, 1, 0},
{1, 1, 1},
{0, 0, 1}
};
n -- ;
while (n)
{
if (n & 1) mul(f1, f1, a); // res = res * a
mul(a, a, a); // a = a * a
n >>= 1;
}
cout << f1[2] << endl;
return 0;
}
8.容斥原理
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 20, mod = 1e9 + 7;
LL A[N];
int down = 1;
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int C(LL a, LL b)
{
if (a < b) return 0;
int up = 1;
for (LL i = a; i > a - b; i -- ) up = i % mod * up % mod;
return (LL)up * down % mod; // 费马小定理
}
int main()
{
LL n, m;
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> A[i];
for (int j = 1; j <= n - 1; j ++ ) down = (LL)j * down % mod;
down = qmi(down, mod - 2, mod);
int res = 0;
for (int i = 0; i < 1 << n; i ++ )
{
LL a = m + n - 1, b = n - 1;
int sign = 1;
for (int j = 0; j < n; j ++ )
if (i >> j & 1)
{
sign *= -1;
a -= A[j] + 1;
}
res = (res + C(a, b) * sign) % mod;
}
cout << (res + mod) % mod << endl;
return 0;
}
9.线性筛
int primes[N], cnt;
bool st[N];
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
10.EXCRT
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
typedef long long ll;
typedef double dl;
using namespace std;
const int N=2e5+7;
const int M=1e9+7;
const int INF=0x3f3f3f3f;
int n;
ll qmul(ll a,ll b,ll mod){
return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1,y=0;
return a;
}
ll g=exgcd(b,a%b,x,y);
ll tx=x;
x=y;
y=tx-(a/b)*y;
return g;
}
ll excrt(ll* m,ll* a){
for(int i=2;i<=n;i++){
ll c=a[i]-a[1],x,y;
c=(c%m[i]+m[i])%m[i];
ll g=exgcd(m[1],m[i],x,y);
x=qmul(x,(c/g),m[i]);
x=(x%m[i]+m[i])%m[i];
a[1]=a[1]+qmul(m[1],x,m[1]*(m[i]/g));
m[1]=m[1]/g*m[i];
a[1]=(a[1]%m[1]+m[1])%m[1];
}
return a[1];
}
ll m[100],a[100];
void solve(){
read(n);
fo(i,1,n){
cin>>m[i]>>a[i];
}
cout<<excrt(m,a);
}
int main(){
// int T;read(T);
// for(int i=1;i<=T;i++)
solve();
}
11.原根
#include<cstdio>
#include<cmath>
inline int phi(int n)
{
int zc=n,all=sqrt(n);
for(int i=2;i<=all;i++)
{
if(n%i!=0)continue;
zc=zc/i*(i-1);
while(n%i==0)n/=i;
}
if(n>1)zc=zc/n*(n-1);
return zc;
}
inline int pow(int x,const int y,const int mod)
{
int res=1;
for(int i=1;i<=y;i<<=1,x=(long long)x*x%mod)if(i&y)res=(long long)res*x%mod;
return res;
}
int q[100001];
inline int G(const int m)
{
const int PHI=phi(m);
q[0]=0;
const int limit=sqrt(PHI);int zc=PHI;
for(int i=2;i<=limit;i++)
if(zc%i==0)
{
q[++q[0]]=PHI/i;
while(zc%i==0)zc/=i;
}
if(zc>1)zc=q[++q[0]]=PHI/zc;
for(int g=2;;g++)
{
bool fla=1;
if(pow(g,PHI,m)!=1)continue;
for(int i=1;i<=q[0];i++)
if(pow(g,q[i],m)==1)
{
fla=0;
break;
}
if(fla)return g;
}
}
int m,g;
int main()
{
scanf("%d",&m);
g=G(m);
printf("%d",g);
return 0;
}
12.博弈论
不平等博弈
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
//加法: x + y = {XL + y , x + YL | XR + y , x + YR }
//SN定理:多游戏的 SN 等于子游戏的 SN 的直接加和
double sn[20][20];
double get_sn(int x,int y){
if(sn[x][y]!=inf) return sn[x][y];
double l=-inf,r=inf;
for(int i=1;i<=x/2;i++) l=max(l,get_sn(i,y)+get_sn(x-i,y));//玩家L 操作的后继状态的SN值集合 XL 取最大值
for(int i=1;i<=y/2;i++) r=min(r,get_sn(x,i)+get_sn(x,y-i));//玩家R 操作的后继状态的SN值集合 XR 取最小值
// 1. l < 0 且 r > 0,SN(x) = 0
if(l<0&&r>0) sn[x][y]=0;
// 2. l , r 中间有整数
else if(floor(l+1)>l&&floor(l+1)<r){
if(l>=0) sn[x][y]=floor(l+1);
else sn[x][y]=ceil(r-1);
}
// 3. l , r 中间无整数,优先 k 最小,其次 j 最小,使得 l < j / 2^k < r
else{
int j,k,tmp=1;
bool ok=0;
for(k=1;!ok;k++){
tmp<<=1;
for(j=floor(l*tmp+1);!ok&&j<tmp*r;j++) if(j>l*tmp&&j<r*tmp){
ok=1;
break;
}
}
sn[x][y]=1.0*j/tmp;
}
return sn[x][y];
}
// x>0,L必胜 ; x<0,R必胜 ; x=0,后手必胜(一定注意是后手必胜,千万别记反了)
int main(){
for(int i=1;i<=16;i++) for(int j=1;j<=16;j++) sn[i][j]=inf;
for(int i=1;i<=16;i++) for(int j=1;j<=16;j++) printf("%.0f%c",get_sn(i,j),j==16?'\n':' ');
return 0;
}
nim积
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2000000
using namespace std;
int m[2][2]={0,0,0,1};
int Nim_Mult_Power(int x,int y){
if(x<2)
return m[x][y];
int a=0;
for(;;a++)
if(x>=(1<<(1<<a))&&x<(1<<(1<<(a+1))))
break;
int m=1<<(1<<a);
int p=x/m,s=y/m,t=y%m;
int d1=Nim_Mult_Power(p,s);
int d2=Nim_Mult_Power(p,t);
return (m*(d1^d2))^Nim_Mult_Power(m/2,d1);
}
int Nim_Mult(int x,int y){
if(x<y)
return Nim_Mult(y,x);
if(x<2)
return m[x][y];
int a=0;
for(;;a++)
if(x>=(1<<(1<<a))&&x<(1<<(1<<(a+1))))
break;
int m=1<<(1<<a);
int p=x/m,q=x%m,s=y/m,t=y%m;
int c1=Nim_Mult(p,s),c2=Nim_Mult(p,t)^Nim_Mult(q,s),c3=Nim_Mult(q,t);
return (m*(c1^c2))^c3^Nim_Mult_Power(m/2,c1);
}
int main(){
int t,n,x,y;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int ret=0;
while(n--){
scanf("%d%d",&x,&y);
ret^=Nim_Mult(x,y);
}
if(ret)
puts("Have a try, lxhgww.");
else
puts("Don't waste your time.");
}
return 0;
}
2.组合数学
1.lucas
long long Lucas(long long n, long long m, long long p) {
if (m == 0) return 1;
return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;
}
2.exlucas
int inverse(int x) 函数返回x在模p意义下的逆元
LL CRT(int n, LL* a, LL* m) {
LL M = 1, p = 0;
for (int i = 1; i <= n; i++) M = M * m[i];
for (int i = 1; i <= n; i++) {
LL w = M / m[i], x, y;
exgcd(w, m[i], x, y);
p = (p + a[i] * w * x % mod) % mod;
}
return (p % mod + mod) % mod;
}
LL calc(LL n, LL x, LL P) {
if (!n) return 1;
LL s = 1;
for (int i = 1; i <= P; i++)
if (i % x) s = s * i % P;
s = Pow(s, n / P, P);
for (int i = n / P * P + 1; i <= n; i++)
if (i % x) s = s * i % P;
return s * calc(n / x, x, P) % P;
}
LL multilucas(LL m, LL n, LL x, LL P) {
int cnt = 0;
for (int i = m; i; i /= x) cnt += i / x;
for (int i = n; i; i /= x) cnt -= i / x;
for (int i = m - n; i; i /= x) cnt -= i / x;
return Pow(x, cnt, P) % P * calc(m, x, P) % P * inverse(calc(n, x, P), P) %
P * inverse(calc(m - n, x, P), P) % P;
}
LL exlucas(LL m, LL n, LL P) {
int cnt = 0;
LL p[20], a[20];
for (LL i = 2; i * i <= P; i++) {
if (P % i == 0) {
p[++cnt] = 1;
while (P % i == 0) p[cnt] = p[cnt] * i, P /= i;
a[cnt] = multilucas(m, n, i, p[cnt]);
}
}
if (P > 1) p[++cnt] = P, a[cnt] = multilucas(m, n, P, P);
return CRT(cnt, a, p);
}
3.预处理阶乘计算组合数
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main()
{
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ )
{
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
int n;
scanf("%d", &n);
while (n -- )
{
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}
4.组合数大礼包
struct AC
{
static const int maxn = 1e6+10;
ll f[maxn];
void init(int mod){ //预处理阶乘
f[0] = 1;
for(int i = 1;i<maxn;i++) f[i] = f[i-1]*i%mod;
}
ll ksm(ll a,ll b,ll mod){
ll ans = 1;
while(b){
if(b&1) ans = ans*a%mod;
a = a*a%mod;
b>>=1;
}
return ans;
}
ll C_mod(ll n,ll m,ll mod){//组合数%mod
return f[n] * ksm(f[m] * f[n-m]%mod,mod-2,mod)%mod;
}
ll lucas_mod(ll n,ll m,ll mod){
return m? C_mod(n%mod,m%mod,mod) * lucas_mod(n/mod,m/mod,mod)%mod : 1;
}
ll A(ll n, ll r)//排列数
{
ll sum = 1;
for (int i = 0; i < r; i++)
sum *= n-i;
return sum;
}
ll C(ll n,ll K){ //组合数
if(K>n) return 0;
if(K > n-K) K = n-K;
ll m = 1,s = 1;
for(int i = 0;i<K;i++){
m = m*(n-i);
s = s*(i+1);
}
return m/s;
}
}ac;
5.排列组合
ll A(ll n, ll r)//排列数
{ ll sum = 1;
for (int i = 0; i < r; i++)
sum *= n-i;
return sum;
}
ll C(ll n, ll r)//组合数
{ ll sum = 1;
for (int i = 1; i <= r; i++)
sum = sum * (n+1-i)/i;
return sum;
}
3.计算几何
1.模拟退火
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
const int N = 10005;
int n, x[N], y[N], w[N];
double ansx, ansy, dis;
double Rand() { return (double)rand() / RAND_MAX; }
double calc(double xx, double yy) {
double res = 0;
for (int i = 1; i <= n; ++i) {
double dx = x[i] - xx, dy = y[i] - yy;
res += sqrt(dx * dx + dy * dy) * w[i];
}
if (res < dis) dis = res, ansx = xx, ansy = yy;
return res;
}
void simulateAnneal() {
double t = 100000;
double nowx = ansx, nowy = ansy;
while (t > 0.001) {
double nxtx = nowx + t * (Rand() * 2 - 1);
double nxty = nowy + t * (Rand() * 2 - 1);
double delta = calc(nxtx, nxty) - calc(nowx, nowy);
if (exp(-delta / t) > Rand()) nowx = nxtx, nowy = nxty;
t *= 0.97;
}
for (int i = 1; i <= 1000; ++i) {
double nxtx = ansx + t * (Rand() * 2 - 1);
double nxty = ansy + t * (Rand() * 2 - 1);
calc(nxtx, nxty);
}
}
int main() {
srand(time(0));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &x[i], &y[i], &w[i]);
ansx += x[i], ansy += y[i];
}
ansx /= n, ansy /= n, dis = calc(ansx, ansy);
simulateAnneal();
printf("%.3lf %.3lf\n", ansx, ansy);
return 0;
}
六.动态规划
1.数位DP
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
typedef long long ll;
typedef double dl;
using namespace std;
//给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数
const int N=2e5+7;
const int M=1e9+7;
const int INF=0x3f3f3f3f;
int n,m;
ll dp[21][200][200];
int a[N];
ll dfs(ll pos,ll num,ll mod,ll rem,bool limit){
if (num<0||num>pos*9){
return 0;
}
if (pos==0){
return num==0&&rem==0;
}
if (dp[pos][num][rem]!=-1&&limit==false){
return dp[pos][num][rem];
}
int up=limit?a[pos]:9;
ll sum=0;
for(int i=0;i<=up;i++){
sum+=dfs(pos-1,num-i,mod,(rem*10+i)%mod,limit&&i==a[pos]);
}
if (!limit){
dp[pos][num][rem]=sum;
}
return sum;
}
ll solve(ll x){
int len=0;
while(x){
a[++len]=x%10;
x/=10;
}
ll sum=0;
for(int i=1;i<=len*9;i++){
memset(dp,-1,sizeof(dp));
sum+=dfs(len,i,i,0,1);
}
return sum;
}
int main(){
// ios::sync_with_stdio(0);
// cin.tie(0),cout.tie(0);
// int T;read(T);
// for(int i=1;i<=T;i++)
ll a,b;
cin>>a>>b;
cout<<solve(b)-solve(a-1);
}
&n);
while (n – )
{
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}
### 4.组合数大礼包
```c++
struct AC
{
static const int maxn = 1e6+10;
ll f[maxn];
void init(int mod){ //预处理阶乘
f[0] = 1;
for(int i = 1;i<maxn;i++) f[i] = f[i-1]*i%mod;
}
ll ksm(ll a,ll b,ll mod){
ll ans = 1;
while(b){
if(b&1) ans = ans*a%mod;
a = a*a%mod;
b>>=1;
}
return ans;
}
ll C_mod(ll n,ll m,ll mod){//组合数%mod
return f[n] * ksm(f[m] * f[n-m]%mod,mod-2,mod)%mod;
}
ll lucas_mod(ll n,ll m,ll mod){
return m? C_mod(n%mod,m%mod,mod) * lucas_mod(n/mod,m/mod,mod)%mod : 1;
}
ll A(ll n, ll r)//排列数
{
ll sum = 1;
for (int i = 0; i < r; i++)
sum *= n-i;
return sum;
}
ll C(ll n,ll K){ //组合数
if(K>n) return 0;
if(K > n-K) K = n-K;
ll m = 1,s = 1;
for(int i = 0;i<K;i++){
m = m*(n-i);
s = s*(i+1);
}
return m/s;
}
}ac;
5.排列组合
ll A(ll n, ll r)//排列数
{ ll sum = 1;
for (int i = 0; i < r; i++)
sum *= n-i;
return sum;
}
ll C(ll n, ll r)//组合数
{ ll sum = 1;
for (int i = 1; i <= r; i++)
sum = sum * (n+1-i)/i;
return sum;
}
3.计算几何
1.模拟退火
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
const int N = 10005;
int n, x[N], y[N], w[N];
double ansx, ansy, dis;
double Rand() { return (double)rand() / RAND_MAX; }
double calc(double xx, double yy) {
double res = 0;
for (int i = 1; i <= n; ++i) {
double dx = x[i] - xx, dy = y[i] - yy;
res += sqrt(dx * dx + dy * dy) * w[i];
}
if (res < dis) dis = res, ansx = xx, ansy = yy;
return res;
}
void simulateAnneal() {
double t = 100000;
double nowx = ansx, nowy = ansy;
while (t > 0.001) {
double nxtx = nowx + t * (Rand() * 2 - 1);
double nxty = nowy + t * (Rand() * 2 - 1);
double delta = calc(nxtx, nxty) - calc(nowx, nowy);
if (exp(-delta / t) > Rand()) nowx = nxtx, nowy = nxty;
t *= 0.97;
}
for (int i = 1; i <= 1000; ++i) {
double nxtx = ansx + t * (Rand() * 2 - 1);
double nxty = ansy + t * (Rand() * 2 - 1);
calc(nxtx, nxty);
}
}
int main() {
srand(time(0));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &x[i], &y[i], &w[i]);
ansx += x[i], ansy += y[i];
}
ansx /= n, ansy /= n, dis = calc(ansx, ansy);
simulateAnneal();
printf("%.3lf %.3lf\n", ansx, ansy);
return 0;
}
六.动态规划
1.数位DP
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define read(x) scanf("%d",&x)
typedef long long ll;
typedef double dl;
using namespace std;
//给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数
const int N=2e5+7;
const int M=1e9+7;
const int INF=0x3f3f3f3f;
int n,m;
ll dp[21][200][200];
int a[N];
ll dfs(ll pos,ll num,ll mod,ll rem,bool limit){
if (num<0||num>pos*9){
return 0;
}
if (pos==0){
return num==0&&rem==0;
}
if (dp[pos][num][rem]!=-1&&limit==false){
return dp[pos][num][rem];
}
int up=limit?a[pos]:9;
ll sum=0;
for(int i=0;i<=up;i++){
sum+=dfs(pos-1,num-i,mod,(rem*10+i)%mod,limit&&i==a[pos]);
}
if (!limit){
dp[pos][num][rem]=sum;
}
return sum;
}
ll solve(ll x){
int len=0;
while(x){
a[++len]=x%10;
x/=10;
}
ll sum=0;
for(int i=1;i<=len*9;i++){
memset(dp,-1,sizeof(dp));
sum+=dfs(len,i,i,0,1);
}
return sum;
}
int main(){
// ios::sync_with_stdio(0);
// cin.tie(0),cout.tie(0);
// int T;read(T);
// for(int i=1;i<=T;i++)
ll a,b;
cin>>a>>b;
cout<<solve(b)-solve(a-1);
}