ACM常用模板V2.0

ACM常用模板V2.0

ACM常用模板

STL

常用函数

数据结构

KMP

Trie 树

AC自动机

树链剖分求LCA

倍增法求LCA

单调栈求矩形个数

带权并查集

字符串哈希

线段树

树状数组求区间连续区间

莫队

克鲁斯卡尔重构树

二.数学

欧拉函数

求逆元

组合数

矩阵加速

三分+秦九邵算法

三.基础算法

RMQ

高精度

离散化/*去重离散化*/

__int128 手写输入输出

二分

线性基求第k小

四. DP

最长公共子序列

数位DP

背包

五. 图论

缩点

2 - sat

差分约束

Johnson 全源最短路

二分图最大匹配(最小点覆盖n==最大匹配数m)

最大流问题

KM算法 二分图最大权匹配(n^3)

欧拉图

STL

vector, 变长数组,倍增的思想

    size()  返回元素个数

    empty()  返回是否为空

    clear()  清空

    front()/back()

    push_back()/pop_back()

    begin()/end()

    []

    支持比较运算,按字典序

pair<int, int>

    first, 第一个元素

    second, 第二个元素

    支持比较运算,以first为第一关键字,以second为第二关键字(字典序)

string,字符串

    size()/length()  返回字符串长度

    empty()

    clear()

    substr(起始下标,(子串长度))  返回子串

    c_str()  返回字符串所在字符数组的起始地址

queue, 队列

    size()

    empty()

    push()  向队尾插入一个元素

    front()  返回队头元素

    back()  返回队尾元素

    pop()  弹出队头元素

priority_queue, 优先队列,默认是大根堆

    push()  插入一个元素

    top()  返回堆顶元素

    pop()  弹出堆顶元素

    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;

stack, 栈

    size()

    empty()

    push()  向栈顶插入一个元素

    top()  返回栈顶元素

    pop()  弹出栈顶元素

deque, 双端队列

    size()

    empty()

    clear()

    front()/back()

    push_back()/pop_back()

    push_front()/pop_front()

    begin()/end()

    []

set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列

    size()

    empty()

    clear()

    begin()/end()

    ++, -- 返回前驱和后继,时间复杂度 O(logn)

    set/multiset

        insert()  插入一个数

        find()  查找一个数

        count()  返回某一个数的个数

        erase()

            (1) 输入是一个数x,删除所有x   O(k + logn)

            (2) 输入一个迭代器,删除这个迭代器

        lower_bound()/upper_bound()

            lower_bound(x)  返回大于等于x的最小的数的迭代器

            upper_bound(x)  返回大于x的最小的数的迭代器

    map/multimap

        insert()  插入的数是一个pair

        erase()  输入的参数是pair或者迭代器

        find()

        []  注意multimap不支持此操作。 时间复杂度是 O(logn)

        lower_bound()/upper_bound()

unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表

    和上面类似,增删改查的时间复杂度是 O(1)

    不支持 lower_bound()/upper_bound(), 迭代器的++,--

bitset, 圧位

    bitset<10000> s;

    ~, &, |, ^

    >>, <<

    ==, !=

    [ ]

    count()  返回有多少个1

    any()  判断是否至少有一个1

    none()  判断是否全为0

    set()  把所有位置成1

    set(k, v)  将第k位变成v

    reset()  把所有位变成0

    flip()  等价于~

    flip(k) 把第k位取反

常用函数

字符串转换整数

atoi(char);把字符串(字符char)转换成整型数的一个函数

stoi;把字符串(string)转换成整数

typedef unsigned long long ll;

ll stoi(char *ss,ll l,ll r)//自己写一个字符串转整数。想知道为什么cstring里包含的stoi()用不了

{

    ll res=0;

    if(l>r)return 0;

    for (ll i=l;i<=r;i++)

    {

        res*=10;

        res+=ss[i]-'0';

    }

    return res;

}

数据结构

KMP

const int N = 1e6+7;int ne[N];

string s1,s2;int len1 ,len2;void init(){

    int ix1 = 0,ix2= -1;

    ne[0] = -1;

    while(ix1<len1){

        if(ix2==-1||s1[ix2]==s1[ix1]){

            ne[++ix1]=++ix2;

        }

        else ix2 = ne[ix2];

    }}void KMP(){

    int ix1 = 0,ix2 =0;

    while(ix1<len2){

        if(ix2==-1||s2[ix1]==s1[ix2]) ix1++,ix2++;

        else ix2 = ne[ix2];

        if(ix2 == len1){

            cout<< ix1-len1<<" " ;

        }

    }}int main(){

    int n;

    cin>>n>>s1>>n>>s2;

    len1 = s1.length();

    len2 = s2.length();

    init();

    KMP();

    return 0;}

Trie 树

const int N = 1e6+7;int son[N][26],cnt[N],idx=0;void insert(string str){

    int p = 0;

    for(int i = 0;str[i];i++){

        int u = str[i] - 'a';

        if(!son[p][u]) son[p][u] = ++idx;

        p = son[p][u];

    }

    cnt[p]++;}int query(string str){

    int p = 0;

    for(int i = 0;str[i];i++){

        int u = str[i] - 'a';

        if(!son[p][u]) return 0;

        p = son[p][u];

    }

return cnt[p];}

AC自动机

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

int trie[N][26]; //字典树代表节点i的儿子j字母的节点编号

int val[N]; //附加信息

int fail[N]; //失配指针

int cnt = 0;  //字典树节点的编号

int f(char s){return s-'a';}

void insert(string s) //建立trie

{   int u = 0; //从根节点开始遍历

    int ch;

for(int i=0;i<s.length();i++){ //遍历所有的字母

ch = f(s[i]);

if(trie[u][ch]==0){

trie[u][ch] = ++cnt;

           // printf("%d %d %d",u,ch,cnt);

}//不存在就创建新节点

u = trie[u][ch]; //更新父亲节点

}

val[u]++; //在叶子标记为单词末尾

//if(val[u]) cout<<val[u]<<" "<<u<<" ";

}

void init_fail(){ //bfs遍历

int u = 0;

queue<int> q;

fail[u] = 0;

for(int i = 0;i<26;i++){  //先把第一层的所有字母放入队列

if(trie[u][i]) {

fail[trie[u][i]] = u;

q.push(trie[u][i]);

}

}

    while(!q.empty()){  //类似bfs

     int rt = q.front();

     q.pop();

     for(int i =0;i<26;i++){

     if(trie[rt][i]){

     fail[trie[rt][i]] = trie[fail[rt]][i];

// 节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)

q.push(trie[rt][i]);

}

else trie[rt][i] = trie[fail[rt]][i];

}

}

}

int ask(string s){

int u = 0;

int ans = 0;

int ch;

for(int i=0;i<s.length();i++){

ch = f(s[i]);

// cout<<ch<<" ";

//printf("%d %d\n",u,ch);

if(trie[u][ch]){

if(val[trie[u][ch]]) {

ans+=val[trie[u][ch]];

val[trie[u][ch]] = 0;

//cout<<ans<<endl;

}

u = trie[u][ch];

}

else {

u = fail[trie[u][ch]];

}

}

return ans ;

}

int main(){

int n;

cin>>n;

while(n--){

string s;

cin>>s;

insert(s);

}

init_fail();

string k;

cin>>k;

cout<<ask(k)<<endl;

return 0;

}

树链剖分求LCA

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

int d[N],f[N],size[N],son[N],top[N];

int head[N],ecnt = 0;

struct edge{

int to,next;

}e[N];

void add(int u,int v){

e[++ecnt].to=v;

e[ecnt].next=head[u];

head[u]=ecnt;

}

void dfs1(int x,int fath){

size[x]=1;d[x]=d[fath]+1;

son[x]=0;f[x]=fath;

for(int i = head[x];i;i=e[i].next){

int to = e[i].to;

if(to==fath) continue;

dfs1(to,x);

size[x]+=size[to];

if(size[son[x]]<size[to]) son[x] = to;

}

}

void dfs2(int x,int topx){

top[x] = topx;

if(son[x]!=0) dfs2(son[x],topx);

for(int i =head[x];i;i=e[i].next){

if(e[i].to!=f[x] && e[i].to!=son[x])

dfs2(e[i].to,e[i].to);

}

}

int lca(int x,int y){

while(top[x]!=top[y]){

if(d[top[x]]<d[top[y]]) swap(x,y);

x = f[top[x]];

}

return d[x]<d[y]?x:y;

}

int main(){

int n,m,s;

cin>>n>>m>>s;

for(int i=0;i<n-1;i++){

int u,v;

cin>>u>>v;

add(u,v);

add(v,u);

}

dfs1(s,0);

dfs2(s,s);

while(m--){

int x,y;

cin>>x>>y;

cout<<lca(x,y)<<"\n";

}

return 0;

}

倍增法求LCA

#include<bits/stdc++.h>

using namespace std;

const int N = 5000000;

int head[N];

int lg[N];

struct edge{

int to,next;

}e[N*4];

int fa[N][30],ecnt=1;

int dep[N];

void addedge(int u,int v){

e[ecnt].to=v;

e[ecnt].next=head[u];

head[u] = ecnt;

ecnt++;

}

void dfs(int son,int father){

dep[son]=dep[father]+1;

fa[son][0] = father;

for(int i = 1;i<=lg[dep[son]];i++){

fa[son][i]=fa[fa[son][i-1]][i-1];

}

for(int i = head[son];i;i=e[i].next){

if(e[i].to!=father) dfs(e[i].to,son);

}

}

int LCA(int x,int y){

if(dep[y]>dep[x]) swap(x,y);

while(dep[x]>dep[y]){

x=fa[x][lg[dep[x]-dep[y]]-1];

}

if(x==y) return x;

for(int i=lg[dep[x]]-1;i>=0;i--)

if(fa[x][i]!=fa[y][i])

x=fa[x][i],y=fa[y][i];

return fa[x][0];

}

int main(){

int n,m,s;

cin>>n>>m>>s;

for(int i = 1; i <= n; ++i)

  lg[i] = lg[i-1] + (1 << lg[i-1] == i);

for(int i = 0;i<n-1;i++){

int u,v;

cin>>u>>v;

addedge(u,v);

addedge(v,u);

}

dfs(s,0);

for(int i = 0;i<m;i++){

int a,b;

cin>>a>>b;

cout<<LCA(a,b)<<endl;

}

return 0;

}

单调栈求矩形个数

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int maxn = 3001;

const int maxm = 3001;

int a[maxn][maxm],h[maxn][maxm];

int st[maxn];

int cnt[maxn][maxn];

int main(){

    int n,m;

    scanf("%d%d",&n,&m);

    for( int i = 1;i <= n;i++ ){

        for( int j = 1;j <= m;j++ ){

            cin>>a[i][j];

        }

    }

    for( int i = 1;i <= m;i++ ){

        for( int j = 1;j <= n;j++ ){

            if( a[j][i]==0 ) h[j][i] = 0;

            else h[j][i] = h[j-1][i] + 1;

        }

    }

    LL ans = 0;

    for( int i = 1;i <= n;i++ ){

        int top = 0;

        for( int j = 1;j <= m;j++ ){

            while( top && h[i][j] < h[i][ st[top] ] )top--;

            st[++top] = j;

            if( top > 1 )cnt[i][j] += cnt[i][ st[top-1] ];

            cnt[i][j] += 1LL * (j - st[top-1])* h[i][j];

            ans += cnt[i][j];

        }

    }

    printf("%lld\n",ans);

    return 0;}

带权并查集

食物链

动物王国中有三类动物 A,B,C,A 吃 B,B 吃 C,C 吃 A。现有 N 个动物,以 1∼N 编号。

每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。

第一种说法是 1 X Y,表示 X 和 Y 是同类。

第二种说法是 2 X Y,表示 X 吃 Y。

此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。

当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

当前的话与前面的某些真的话冲突,就是假话;
当前的话中 X 或 Y 比 N 大,就是假话;
当前的话表示 X 吃 X,就是假话。
你的任务是根据给定的 N 和 K 句话,输出假话的总数。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

int A[N],d[N];

int find(int x){

if(A[x]!=x) {

int t = find(A[x]);

d[x] += d[A[x]];

A[x] = t;

}

return A[x];

}

int main(){

int n,m;

cin>>n>>m;

for(int i = 1;i<=n;i++) A[i] = i;

int res = 0;

for(int i = 1;i<=m;i++){

int t,x,y;

cin>>t>>x>>y;

int px = find(x),py = find(y);

if(x>n||y>n) res++;

else {

if(t==1) {

if(px==py&&(d[x]-d[y])%3)res++;

else if(px!=py){

A[px] =py;

d[px] = d[y]-d[x];

}

}

else {

if(px==py&&(d[x]-d[y]-1)%3) res++;

else if(px!=py){

A[px] = py;

d[px] = d[y]-d[x]+1;

}

}

}

}

cout<<res<<endl;

return 0;

}

字符串哈希

841. 字符串哈希

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。

#include<bits/stdc++.h>

#include<bits/stdc++.h>

using namespace std;const int N = 1e6+7;

typedef unsigned long long ull;

const int I = 131;

ull p[N]={1},h[N],n,q;char s[N];

ull check(int l,int r){

    return h[r]-h[l-1]*p[r-l+1];}int main(){

    scanf("%d%d%s",&n,&q,s+1);

    for(int i = 1;i<=n;i++){

        p[i] = p[i-1]*I;

        h[i] = I*h[i-1] + s[i];

    }

    while(q--){

        int l,r,ll,rr;

        cin>>l>>r>>ll>>rr;

        if(check(l,r)==check(ll,rr)) cout<<"Yes"<<endl;

        else cout<<"No\n";  

    }

    return 0;}

线段树

#include<bits/stdc++.h>using namespace std;const int N = 1e5+7;typedef long long ll;

ll A[N];

ll n,m,p;struct TREE{

ll val,lazy=0,mul=1;}tree[N*4];void pushup(ll rt){

tree[rt].val = (tree[rt*2].val+tree[rt*2+1].val)%p;} void build(ll l,ll r,ll rt){

if(l==r) {

tree[rt].val = A[l];

return ;

}

ll mid = (l+r)/2;

build(l,mid,rt*2);build(mid+1,r,rt*2+1);

pushup(rt);}void pushdown(ll l,ll r,ll rt){

tree[rt*2].val = (tree[rt].mul*tree[rt*2].val+tree[rt].lazy*l)%p;

tree[rt*2+1].val = (tree[rt].mul*tree[rt*2+1].val+tree[rt].lazy*r)%p;

tree[rt*2].mul = (tree[rt].mul*tree[rt*2].mul)%p;

tree[rt*2+1].mul = (tree[rt].mul*tree[rt*2+1].mul)%p;

tree[rt*2].lazy =  (tree[rt*2].lazy*tree[rt].mul+tree[rt].lazy)%p;

tree[rt*2+1].lazy =  (tree[rt*2+1].lazy*tree[rt].mul+tree[rt].lazy)%p;

tree[rt].lazy = 0;

tree[rt].mul = 1; }void updata1(ll L,ll R,ll C,ll l,ll r,ll rt){

if(l>=L&&r<=R){

tree[rt].lazy = (C+tree[rt].lazy)%p;

tree[rt].val = (tree[rt].val+C*(r-l+1))%p;

return;

}

ll mid = (l+r)/2;

pushdown(mid-l+1,r-mid,rt);

if(mid>=L) updata1(L,R,C,l,mid,rt*2);

if(mid<R) updata1(L,R,C,mid+1,r,rt*2+1);

pushup(rt); }void updata2(ll L,ll R,ll C,ll l,ll r,ll rt){

if(l>=L&&r<=R){

tree[rt].lazy = tree[rt].lazy*C%p;

tree[rt].mul = tree[rt].mul*C%p;

tree[rt].val = (tree[rt].val*C)%p;

return;

}

ll mid = (l+r)/2;

pushdown(mid-l+1,r-mid,rt);

if(mid>=L) updata2(L,R,C,l,mid,rt*2);

if(mid<R) updata2(L,R,C,mid+1,r,rt*2+1);

pushup(rt); }

ll ask(ll L,ll R,ll l,ll r,ll rt){

if(l>=L&&r<=R){

return tree[rt].val%p;

}

ll mid = (l+r)/2;

if(l>R||r<L)return 0;

pushdown(mid-l+1,r-mid,rt);

return (ask(L,R,l,mid,rt*2)+ask(L,R,mid+1,r,rt*2+1))%p;}

int main(){

cin>>n>>p;

for(ll i = 1;i<=n;i++){

cin>>A[i];

}

cin>>m;

build(1,n,1);

while(m--){

ll x,y,a,c;

cin>>a>>x>>y;

if(a==1) {

cin>>c;

updata2(x,y,c,1,n,1);

}

else if(a==2){

cin>>c;

updata1(x,y,c,1,n,1);

}

else cout<<ask(x,y,1,n,1)%p<<'\n';

}

return 0;}

树状数组求区间连续区间

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

typedef long long ll;

int tr[N],n;

void add(int ix,int x){

for(int i = ix;i<=n;i+=(i&-i)){

tr[i] += x;

}

}

int ask(int ix){

int res = 0;

for(int i = ix;i>0;i-=(i&-i)){

res += tr[i];

}

return res;

}

struct node{

int l,r,i;

bool operator<(const node a)const{

return l<a.l;

}

}B[N];

int pos[N];

int ans[N],A[N];

int main(){

//ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

int T;

cin>>T;

while(T--){

int m;

scanf("%d%d",&n,&m);

for(int i = 0;i<=4*n;i++) tr[i] = 0;

for(int i = 1;i<=n;i++) {

scanf("%d",&A[i]);

pos[A[i]] = i;

}

for(int i = 0;i<m;i++){

int l,r;scanf("%d%d",&l,&r);

B[i] = {l,r,i};

}

sort(B,B+m);

pos[0] = n + 1, pos[n + 1] = n + 1;

for(int i = 1;i<=n;i++){

if(pos[A[i]-1]>i&&pos[A[i]+1]>i)

add(i,1);

if(pos[A[i]-1]<i&&pos[A[i]+1]<i)

add(i,-1);

}

int ix = 0;

for(int i = 1;i<=n;i++){

while(ix<m&&i==B[ix].l){

ans[B[ix].i] = ask(B[ix].r);

ix++;

}

add(i,-1);

if(pos[A[i]+1]>i) add(pos[A[i]+1],1);

if(pos[A[i]-1]>i) add(pos[A[i]-1],1);

}

for(int i = 0;i<m;i++){

printf("%d\n",ans[i]);

}

  }

return 0;

}

莫队

区间求不同的数+单点修改

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

typedef long long ll;

int n,m;

int block;

int A[N],cnt[N],cur = 0,ans[N];

inline int read(){

    int a=0,f=0;char c=getchar();

    for(;c<'0'||c>'9';c=getchar())if(c=='-')f=1;

    for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';

    return f?-a:a;

}

struct node{

ll l,r,id,t;

}qq[N];

struct nod{

int l,r;

}qr[N];

int cntqq,cntqr;

bool cmp (const node &a, const node &b)

{

return a.l / block == b.l / block ? a.r / block == b.r / block ? a.t < b.t : a.r < b.r : a.l < b.l;

}

inline void add(int i,int x){

if(x==1){

cur += !cnt[A[i]]++;

}

else cur -= !(--cnt[A[i]]);

}

inline void upd(int ll,int rr,int t){

if(ll <= qr[t].l && rr >= qr[t].l){

add(qr[t].l,-1);

cur += !cnt[qr[t].r]++;

}

A[qr[t].l] = A[qr[t].l] ^ qr[t].r;

qr[t].r = A[qr[t].l] ^ qr[t].r;

A[qr[t].l] = A[qr[t].l] ^ qr[t].r;

}

int main(){

//ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

scanf("%d%d",&n,&m);

block = pow(n,0.666);

for(int i = 1;i<=n;i++) scanf("%lld",A+i);

for(int i = 0;i<m;i++){

int l,r;

char ch[5];

scanf("%s%d%d",ch,&l,&r);

if(ch[0]=='Q'){

qq[cntqq] = {l,r,cntqq,cntqr};

cntqq++;

}       

else {

qr[++cntqr] = {l,r};

}

}

sort(qq,qq+cntqq);

int l = 1,r = 0,t = 0;

for(int i = 0;i<cntqq;i++){

int ll = qq[i].l;

int rr = qq[i].r;

int tt = qq[i].t;

while(l>ll) add(--l,1);

while(r>rr) add(r--,-1);

while(l<ll) add(l++,-1);

while(r<rr) add(++r,1);

while(t<tt) upd(ll,rr,++t);

while(t>tt) upd(ll,rr,t--);

ans[qq[i].id] = cur;

}

for(int i = 0;i<cntqq;i++){

printf("%d\n",ans[i]);

}

return 0;

}

克鲁斯卡尔重构树

求路径最大值最小

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

typedef long long ll;

int A[N];

vector<int> v[N];

int sz[N],dep[N],son[N],fa[N],top[N];

int find(int x){

if(x == A[x]) return A[x];

else return A[x] = find(A[x]);

}

void dfs(int u,int f){

dep[u] = dep[f] + 1;

sz[u] = 1;

fa[u] = f;

for(int i : v[u]){

if(i == f) continue;

dfs(i,u);

sz[u] += sz[i];

if(sz[son[u]]<sz[i]) son[u] = i;

}

}

void dfs1(int u,int topx){

top[u] = topx;

if(son[u]) dfs1(son[u],topx);

for(int i : v[u]){

if(i == fa[u] || son[u] == i) continue;

dfs1(i,i);

}

}

int LCA(int a,int b){

while(top[a] != top[b]){

if(dep[top[a]] < dep[top[b]]) swap(a,b);

a = fa[top[a]];

}

return dep[a]<dep[b]?a:b;

}

struct node{

int a,b,c;

bool operator < (const node p) const {

return c < p.c;

}

}e[N];

int key[N];

int main(){

ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

int n,m;

cin>>n>>m;

for(int i = 1;i<=m;i++){

int a,b,c;

cin>>a>>b>>c;

e[i] = {a,b,c};

}

sort(e+1,e+m+1);

int k = n;

for(int i = 1;i<=n*2;i++) A[i] = i;

for(int i = 1;i<=m;i++){

int a = e[i].a , b =e[i].b,c =e[i].c;

int na = find(a);

int nb = find(b);

if(na!=nb){

k++;

A[nb] = A[na] = k;

v[na].push_back(k);

v[k].push_back(na);

v[nb].push_back(k);

v[k].push_back(nb);

key[k] = c;

}

}

for(int i = k;i;i--)if(!sz[i]) dfs(i,i),dfs1(i,i);

int q;

cin>>q;

while(q--){

int x,y;

cin>>x>>y;

if(find(x)!=find(y)) {

cout<<"impossible\n";

}

else {

cout<<key[LCA(x,y)]<<'\n';

}

}

return 0;

}

二.数学

欧拉函数

求单个

const int N = 1e6+7;

int main(){

    int T;

    cin>>T;

    while(T--){

        ll x;

        cin>>x;

        ll res = x;

        for(ll i = 2;i<=x/i;i++){

            if(x%i==0){

                while(x%i==0) x/=i;

                res = res/i*(i-1);

            }

        }

        if(x>1) res = res/x*(x-1);

        cout<<res<<endl;

    }

    return 0;}

线性求

void get_eulers(int n){

    phi[1] = 1;

    for (int i = 2; i <= n; i++)

    {

        if (!st[i])

        {

            primes[cnt++] = i;

            phi[i] = i - 1;

        }

        for (int j = 0; primes[j] <= n / i; j++)

        {

            st[primes[j] * i] = true;

            if (i % primes[j] == 0)

            {

                phi[primes[j] * i] = phi[i] * primes[j];

                break;

            }

            phi[primes[j] * i] = phi[i] * (primes[j] - 1);

        }

 求逆元

1.费马小定理 inv[i] = pow(i,p-2) p为质数
2.exgcd

ll exgcd(ll a,ll b,ll & x,ll &y){

if(!b){

x = 1;

y = 0;

return a;

}

ll d = exgcd(b,a%b,y,x);

y -= x*(a/b);

return d;}

3 . 线性递推

void init(){

inv[1] = 1;

for(int i = 2;i <= N;i++){

inv[i] = (p - p / i) * inv[p % i] % p;

}}

组合数

  1. 递推

void init(){

    for(int i=0;i<4000;i++){

        for(int j=0;j<=i;j++){

            if(!j) c[i][j]=1;

            else c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;

        }

    }}

2.公式 Cba=a!/b!(a−b)!=a!∗b!−1∗(a−b)!−1
即求(b!)逆元和(a-b)!-1的逆元,递推即可
3.卢卡斯公式

int quick_power(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;}

int C(int a, int b, int p){

    if (b > a) {

        return 0;

    }

    int res = 1;

    for (int i = 1, j = a; i <= b; i++, j--) {

        res = (ll)res * j % p;

        res = (ll)res * quick_power(i, p - 2, p) % p;

    }

    return res;}int lucas(ll a, ll b, int p)//a个里面调b个{

    if (a < p && b < p) {

        return C(a, b, p);

    }

    return (ll)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;}

矩阵加速

斐波那契求和

#include<bits/stdc++.h>using namespace std;const int N = 3;typedef long long ll;

ll n,m;

ll A[N][N] =    // 上述矩阵 A{

    {2, 0, -1},

    {1, 0, 0},

    {0, 1, 0}};

ll S[N] = {2,1,0};void cal(ll A[][N],ll B[][N]){

    ll nw[N][N]={0};

    for(ll i = 0;i<N;i++){

        for(ll j = 0;j<N;j++){

            for(ll k = 0;k<N;k++){

                nw[i][j] += A[i][k]*B[k][j]%m;

            }

        }

    }

    for(ll i = 0;i<N;i++){

        for(ll j = 0;j<N;j++){

            A[i][j] = nw[i][j]%m;

        }

    } }void cal(ll A[],ll B[][N]){

    ll nw[N] = {0};

    for(ll i = 0;i<N;i++){

        for(ll j = 0;j<N;j++){

            nw[i] += A[j]*B[i][j]%m;

        }

    }

    for(ll i = 0;i<N;i++){

        A[i] = nw[i] % m;

    }}int  main(){

    cin>>n>>m;

    while(n){

        if(n&1){

            cal(S,A);

        }

        cal(A,A);

        n>>=1;

    }

    cout<<((S[2]%m+m)%m)<<endl;

return 0;}

三分+秦九邵算法

#include<bits/stdc++.h>

using namespace std;

const double eps=1e-7;//其实一般精度*0.1=1e-6就可以了

int n;

double L,R;

double a[15];

//秦九韶算法从里到外逐层计算一次多项式的值

double F(double x)

{

 double sum=0;

 for(int i=n;i>=0;i--)

 sum=sum*x+a[i];

 return sum;

}

int main()

{

 cin>>n>>L>>R;

 for(int i=n;i>=0;i--) cin>>a[i];

 while(fabs(L-R)>=eps)

 {

  double mid=(L+R)/2;

  if(F(mid+eps)>F(mid-eps)) L=mid;//舍弃左区间

  else R=mid;//舍弃右区间

 }

 printf("%.5lf",R);

 return 0;

}

三.基础算法

RMQ

ll A[N];

ll n;

ll fa[N][30];int main(){

    cin>>n;

    for(int i = 1;i<=n;i++){

        cin>>A[i];

        fa[i][0] = A[i];

    }

    for(int i = 1;i<25;i++){

        for(int j = 1;(1<<(i-1))+j<=n;j++){

            fa[j][i] = max(fa[j][i-1],fa[j+(1<<(i-1))][i-1]);

        }

    }

    int m;

    cin>>m;

    while(m--){

        ll x,y;

        cin>>x>>y;

        int t=log2(y-x+1);

        cout<<max(fa[x][t],fa[y-(1<<t)+1][t])<<endl;

    }

    return 0;}

高精度

#include<iostream>

#include<string>

#include<cstring>

#include<cstdio>

using namespace std;

const int N = 1005;

struct bign

{

    int len,s[N];

    bign()  {  memset(s,0,sizeof(s));  len=1;  }

    bign(int num)  {  *this=num; }

    bign(char *num) { *this=num; }

    bign operator =(int num)

    {

        char c[N];

        sprintf(c,"%d",num);

        *this=c;

        return *this;

    }

    bign operator =(const char *num)

    {

        len=strlen(num);

        for (int i=0;i<len;i++) s[i]=num[len-1-i]-'0';

        return *this;

    }

    string str()

    {

        string res="";

        for (int i=0;i<len;i++) res=(char)(s[i]+'0')+res;

        return res;

    }

    void clean()

    {

        while (len>1&&!s[len-1]) len--;

    }

    bign operator +(const bign &b)

    {

        bign c;    

        c.len=0;

        for (int i=0,g=0;g||i<len||i<b.len;i++)

        {

            int x=g;

            if (i<len) x+=s[i];

            if (i<b.len) x+=b.s[i];

            c.s[c.len++]=x%10;

            g=x/10;

        }

        return c;

    }

    bign operator -(const bign &b)

    {

        bign c;

        c.len=0;

        int x;     

        for (int i=0,g=0;i<len;i++)

        {

            x=s[i]-g;

            if (i<b.len) x-=b.s[i];

            if (x>=0) g=0;

            else{          

                x+=10;

                g=1;

            };

            c.s[c.len++]=x;

        }

        c.clean();

        return c;

    }

    bign operator *(const bign &b)

    {

        bign c;

        c.len=len+b.len;

        for (int i=0;i<len;i++) for (int j=0;j<b.len;j++) c.s[i+j]+=s[i]*b.s[j];

        for (int i=0;i<c.len-1;i++) { c.s[i+1]+=c.s[i]/10; c.s[i]%=10; }

        c.clean();

        return c;  

    }

    bool operator <(const bign &b)

    {

        if (len!=b.len) return len<b.len;

        for (int i=len-1;i>=0;i--)

             if (s[i]!=b.s[i]) return s[i]<b.s[i];

        return false;

    }

    bign operator +=(const bign &b)

    {

        *this=*this+b;

        return *this;

    }

    bign operator -=(const bign &b)

    {

        *this=*this-b;

        return *this;

    }  

};

istream& operator >>(istream &in,bign &x)

{

  string s;

  in>>s;

  x=s.c_str();

  return in;

}

ostream& operator <<(ostream &out,bign &x)

{

    out<<x.str();

    return out;

}

int main(){

    bign a,b,c;

    ios::sync_with_stdio(false);

    cin>>a>>b;

//    cout<<a<<endl;

//    cout<<b<<endl;

    c=a+b;

    cout<<c<<endl;

    return 0;

}

离散化/*去重离散化*/

int tot = 0;

        scanf("%d",&n);

        over(i,1,n){

            scanf("%d",&a[i]);

            b[tot++] = a[i];

        }

        sort(b,b+tot);

        int res = unique(b,b+tot)-b;

        over(i,1,n)

            a[i] = lower_bound(b,b+res,a[i]) - b;

/*主要注意unique的用法,返回的是一个迭代器,而且unique并没有真正删除元素*/

__int128 手写输入输出

__int128 read(){

__int128 res = 0;

int sign = 1;

char ch;

if((ch=getchar())=='-1') sign = -1;

else res = res*10 + ch-'0';

while((ch=getchar())>='0'&&ch<='9'){

res = res*10 + ch-'0';

}

res *= sign;

return res;} void print(__int128 x ){

if(x<0){

x = -x;

printf("-");

}

if(x>9) print(x/10);

putchar(x%10 + '0');}

二分

1答案的最大值,[l, r]划分成[l, mid]和[mid + 1, r]

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;}

2.答案的最小值,[l, r]划分成[l, mid - 1]和[mid, r]

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;}

线性基求第k小

#include<bits/stdc++.h>

#define reg register

using namespace std;

typedef long long ll;

const int MN=60;

ll a[61],tmp[61];

bool flag;

void ins(ll x){

    for(reg int i=MN;~i;i--)

        if(x&(1ll<<i))

            if(!a[i]){a[i]=x;return;}

            else x^=a[i];

    flag=true;

}

bool check(ll x){

    for(reg int i=MN;~i;i--)

        if(x&(1ll<<i))

            if(!a[i])return false;

            else x^=a[i];

    return true;

}

ll qmax(ll res=0){

    for(reg int i=MN;~i;i--)

        res=max(res,res^a[i]);

    return res;

}

ll qmin(){

    if(flag)return 0;

    for(reg int i=0;i<=MN;i++)

        if(a[i])return a[i];

}

ll query(ll k){

    reg ll res=0;reg int cnt=0;

    k-=flag;if(!k)return 0;

    for(reg int i=0;i<=MN;i++){

        for(int j=i-1;~j;j--)

            if(a[i]&(1ll<<j))a[i]^=a[j];

        if(a[i])tmp[cnt++]=a[i];

    }

    if(k>=(1ll<<cnt))return -1;

    for(reg int i=0;i<cnt;i++)

        if(k&(1ll<<i))res^=tmp[i];

    return res;

}

int main(){

    int n;ll x;scanf("%d",&n);

    for(int i=1;i<=n;i++)scanf("%lld",&x),ins(x);

    printf("%lld\n",qmax());

    return 0;

}

最长公共子序列

#include<iostream>

using namespace std;

int A[100001];

int B[100001];

long long  map[100001];

int main(){

int n;

cin>>n;

for(int i=1;i<=n;i++){

cin>>A[i];

map[A[i]] = i;

}

for(int i=1;i<=n;i++){

cin>>B[i];

}

for(int i = 1;i<=n;i++){

B[i] = map[B[i]];

A[i] = map[A[i]];

}

int ans[100000];

int len =0;

ans[1] = B[1];

int in = 1;

for(int i = 2;i<=n;i++){

if(B[i]>ans[in]) {

ans[++in] = B[i];

}

else {

int k = lower_bound(ans+1,ans+1+in,B[i])-ans;

ans[k] = B[i];

}

}

cout<<in;

return 0;

}

数位DP

int dp[2000][10],A[N]; long long dfs(int pos ,int limit,int lead,int pre){

    long long ans = 0;

    if(pos<0) return 1;

    if(!limit&&!lead&&dp[pos][pre]!=-1) return dp[pos][pre];

    int up = limit?A[pos]:9;

    for(int i = 0;i<=up;i++){

        if(lead||abs(pre-i)>=2) ans+=dfs(pos-1,limit&&i==up,lead&&i==0,i);

    }

    if(!limit&&!lead) dp[pos][pre] = ans;

return ans;}

long long slove(long long x){

    int pos = 0;

    memset(dp,-1,sizeof(dp));

    while(x){

        A[pos++] = x%10;

        x/=10;

    }

return dfs(pos-1,1,1,-2);}

背包

01 背包

#include <iostream>

using namespace std;

const int N=1010;

int v[N],w[N],dp[N]={0};

int main()

{

    int n,V;

    cin >> n >> V;

    for(int i=1;i<=n;i++)

    {

        cin >> v[i] >> w[i];

    }

    for(int i=1;i<=n;i++)

        for(int j=V;j;j--)

        {

            if(j>=v[i])dp[j]=max(dp[j-v[i]]+w[i],dp[j]);//这里改用了一维的;

        }

    cout << dp[V];

    return 0;

}

完全背包

#include <iostream>

#include <algorithm>

using namespace std;

const int N= 1010;

int n,m;

int dp[N];

int v[N];

int w[N];

int main()

{

    cin >> n >> m;

    for(int i = 1;i <= n;i++){

        cin >> v[i] >> w[i];

    }

    for(int i = 1;i<=n;i++){

        for(int j = v[i];j <= m ;j++){

            arr[j] = max(dp[j] , dp[j-v[i]]+w[i]);//每次保留最大的

        }

    }

    cout <<dp[m];

    return 0;   

}

多重背包(二进制优化)

using namespace std;

int n,m,v,w,s;

int dp[3000];

vector<int>ve;

int main()

{

cin>>n>>m;

for(int i=0;i<n;i++)

{

cin>>v>>w>>s;

ve.clear() ;

for(int q=1;q<s;q<<=1)//将 s 用 二进制的数表示 每次将 q 乘以2,保证是2的整数次方

{

s-=q;

ve.push_back(q);

}

if(s) ve.push_back(s);  //如果最后s有剩余,就单独放入;

for(int k=0;k<ve.size();k++)

{

for(int j=m;j>=v*ve[k];j--)//遍历ve中的数据作为01背包,每个数据只会有取和不去的两种选项

{

dp[j]=max(dp[j],dp[j-ve[k]*v]+ve[k]*w);

}

}

}

cout<<dp[m]<<endl;

return 0;

}

混合背包

#include<iostream>

#include<algorithm>

#include<string.h>

#include<vector>

using namespace std;

int n,m,v,w,s;

int dp[3000];

vector<int>ve;

int main()

{

cin>>n>>m;

for(int i=0;i<n;i++)

{

cin>>w>>v>>s;

ve.clear() ;

if(s>0){  //如果是多重的用二进制进行优化

for(int p=1;p<s;p<<=1)

{

ve.push_back(p);

s=s-p;

}

if(s) ve.push_back(s);

}

if(!s)//完全背包

{

for(int j=w;j<=m;j++)

{

dp[j]=max(dp[j],dp[j-w]+v);

}

}

else if(s>0){//多重背包

for(int k=0;k<ve.size();k++)

{

for(int j=m;j>=w*ve[k];j--)

dp[j]=max(dp[j],dp[j-w*ve[k]]+ve[k]*v);

}

}

else{//01背包

for(int j=m;j>=w;j--)

{

dp[j]=max(dp[j],dp[j-w]+v);

}

}

}

cout<<dp[m]<<endl;

return 0;

}

二维费用的背包问题

#include<iostream>

#include<algorithm>

#include<string.h>

using namespace std;

int n,V,M;

int dp[1500][1500];

int v,m,w;

int main()

{

cin>>n>>V>>M;

for(int i=0;i<n;i++)

{

cin>>v>>w>>m;

for(int j=V;j>=v;j--)//两个循环分别遍历体积的质量遍历的规则和一维的一样

{

for(int k=M;k>=w;k--)

{

dp[j][k]=max(dp[j][k],dp[j-v][k-w]+m);

}

}

}

cout<<dp[V][M]<<endl;

return 0;

}

背包问题求方案数

#include<string.h>

#include<iostream>

#include<algorithm>

using namespace std;

int dp[1090];

int flag[1090];

int n,v,V,m;

const int mod=1e9+7;

int main()

{

cin>>n>>V;

flag[0]=1;

for(int i=0;i<n;i++)

{

cin>>v>>m;

for(int j=V;j>=v;j--)

{   

int s=0;

int t=max(dp[j],dp[j-v]+m);//还和之前的一样遍历只是中间需要记录路径数所以不能直接赋值

if(t==dp[j]) s+=flag[j];//当t=dp[j]时s加上flag【j】

if(t==dp[j-v]+m) s=s+flag[j-v];//当t=dp[j-v]+m时s加上flag[j-v]

flag[j]=s%mod;//注意取mod 只是因为  有可能两种不同的路径取出来的最大值是相同的

dp[j]=t;

}

}

int maxn=-1;

int sum=0;

for(int i=0;i<=V;i++) maxn=max(maxn,dp[i]);//遍历最大值

for(int i=0;i<=V;i++)

{

if(dp[i]==maxn)

{

sum=(sum+flag[i])%mod;//路径相加

}

}

cout<<sum<<endl;

return 0;

}

  • 图论

缩点

//强连通分量 kosaraju

// 洛谷 P2341

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7; int n,m;

struct edge{

int to;

int next;

}e[N],e_[N];

int w[N];

int head[N],ecnt = 0;

int head_[N],ecnt_ = 0;

int f[N]; //表示第i个点是属于f【i】的强连通分量

int order[N];

int vis[N];

int A[N],B[N];

long long  ans;

long long  dp[N]={0};

int t = 0;

void  add(int u,int v){

e[++ecnt].to = v;

e[ecnt].next = head[u];

head[u] = ecnt;

}

void add_(int u,int v){

e_[++ecnt_].to = v;

e_[ecnt_].next = head_[u];

head_[u] = ecnt_;

}

void dfs1(int x){

vis[x] =1;

for(int i = head[x];i;i = e[i].next){

int u = e[i].to;

if(!vis[u])dfs1(u);

}

order[++t] = x;

}

void dfs2(int x,int y){

vis[x]= 1 ;

for(int i = head_[x];i;i = e_[i].next){

int u = e_[i].to;

if(!vis[u]){

dfs2(u,y);

}

}f[x] = y;

}

void kosaraju(){

 //先从前往后遍历图,求出order数组

 //再跟据order从后往前遍历反图,记录f[i](每个点隶属的强连通分量)

 for(int i = 1;i<=n;i++) {

  if(!vis[i])dfs1(i);

 }  

memset(vis,0,sizeof(vis));

 for(int i = t;i>=1;i--){

  if(!vis[order[i]])

dfs2(order[i],order[i]);

 }

memset(vis,0,sizeof(vis));

}

void build(){

ecnt = 1;

memset(head,0,sizeof(head));

for(int i =0 ;i<m;i++){

int a = A[i],b = B[i];

if(f[A[i]]!=f[B[i]]) add(f[A[i]],f[B[i]]);

else  {

if(!vis[a]&&f[a]!=a) w[f[a]] += w[a];

if(!vis[b]&&f[a]!=b) w[f[a]] += w[b];

vis[a] = vis[b] = 1;

}

}

}

void dfs3(int x){

if(dp[x]) return ;

for(int i = head[x];i;i = e[i].next){

if(!dp[e[i].to]) dfs3(e[i].to);

dp[x] = max(dp[x],dp[e[i].to]);

}

dp[x] += w[x];

}

int dag(){

ans = -INT_MAX;

for(int i = 1;i<=n;i++){

if(!dp[f[i]]) dfs3(f[i]),ans = max(ans,dp[f[i]]);

}

}

int main(){

cin>>n>>m;

for(int i = 1;i<=n;i++) cin>>w[i];

for(int i = 0;i<m;i++){

int a,b;

scanf("%d %d",&a,&b);

A[i] = a;

B[i] = b;

add(a,b);

add_(b,a);

}

kosaraju();

build();

dag();

cout<<ans<<endl;

return 0;

}

2 - sat

#include<bits/stdc++.h>

using namespace std;

const int N = 3e6+7;

struct node{

int to,next;

}e[N],e2[N];

int head[N],head2[N],vis[N],n,m,ans=0,A[N],B[N],ecnt=0,ecnt2=0,f[N],t=0,ord[N]={0};

int ru[N],chu[N];

void add(int u,int v){

e[++ecnt].to = v;e[ecnt].next = head[u];head[u] = ecnt;

}

void add2(int u,int v){

e2[++ecnt2].to = v;e2[ecnt2].next = head2[u];head2[u] = ecnt2;

}

void dfs1(int x){

vis[x]=1;

for(int i = head[x];i;i=e[i].next) {

int u = e[i].to;

if(!vis[u]) dfs1(u);

}

ord[++t] = x;

}

int cnt = 0;

void dfs2(int x){

f[x] = cnt;

vis[x]=1;

for(int i = head2[x];i;i=e2[i].next){

int u = e2[i].to;

if(!vis[u])dfs2(u);

}

}

void kosaraju(){

for(int i = 1;i<=2*n;i++) if(!vis[i]) dfs1(i);

memset(vis,0,sizeof(vis));

for(int i = 2*n;i>=1;i--) if(!vis[ord[i]]) cnt++,dfs2(ord[i]);

memset(vis,0,sizeof(vis));

}

int main(){

cin>>n>>m;

for(int i = 0;i<m;i++){

int x,f1,y,f2;

scanf("%d%d%d%d",&x,&f1,&y,&f2);

add(x+n*(f1&1),y+n*(f2^1));

add(y+n*(f2&1),x+n*(f1^1));

add2(y+n*(f2^1),x+n*(f1&1));

add2(x+n*(f1^1),y+n*(f2&1));

}

kosaraju();

for(int i = 1;i<=n;i++){

if(f[i]==f[i+n]) {

cout<<"IMPOSSIBLE"<<endl;

return 0;

}

}

cout<<"POSSIBLE"<<endl;

for(int i=1;i<=n;i++) {

cout<<(f[i]>f[i+n])<<" ";

}

return 0;

}

差分约束

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

int n;

struct edge{

int to,next,w;

}e[N];

int vis[N];

int cnt[N];

int ecnt = 0,head[N];

void add(int u,int v,int w){

e[++ecnt].to=v;

e[ecnt].w=w;

e[ecnt].next=head[u];

head[u] = ecnt;

}

int dist[N];

bool spfa(){

dist[0] = 0;

queue<int> q;

q.push(0);

vis[0] = 1;

cnt[0]++;

while(!q.empty()){

int k = q.front();

q.pop();

vis[k] = 0;

for(int i = head[k];i;i=e[i].next){

int to = e[i].to;

int w = e[i].w;

if( dist[to] < dist[k] + w){

dist[to] = dist[k]+w;

if(!vis[to]){

vis[to] = 1;

if(cnt[to]>n) return 0;

cnt[to]++;

q.push(to);

}

}

}

}

return 1;

}

int main(){

int m;

cin>>n>>m;

for(int i=0;i<m;i++){

int a,b,c;

cin>>a>>b>>c;

add(a,b,-c);

}

for(int i = 1;i <=n;i++) add(0,i,0),dist[i]=-10000;

if(spfa()==0){

cout<<"NO"<<endl;

}

else

for(int i = 1;i<=n;i++) cout<<dist[i]<<" ";

return 0;    

}

Johnson 全源最短路

#include<bits/stdc++.h>

using namespace std;

const int N = 6e3+7;

typedef long long ll;

int inf = 1e9;

struct node{

int dis,id;

bool operator<(const node a)const{

return dis>a.dis;

}

};

struct edge{

int to,next,w;

}e[100001];

ll head[N],ecnt=0,vis[N],dp[N],check_cir[N],n,m,dis[N];

void add(int u,int v,int w){

e[++ecnt].w = w;

e[ecnt].to = v;

e[ecnt].next = head[u];

head[u] = ecnt;

}

void dijkstra(int s){

priority_queue<node> q;

memset(vis,0,sizeof(vis));

for(int i = 0;i<=n;i++) dis[i] = inf;

q.push({0,s});

dis[s] = 0;

while(!q.empty()){

int u = q.top().id;

ll d = q.top().dis;

q.pop();

if(vis[u]) continue;

vis[u] = 1;

for(int i = head[u]; i ;i = e[i].next){

int to = e[i].to,w = e[i].w;

if(dis[to]>w+dis[u]){

dis[to] = w + dis[u];

if(!vis[to])

q.push({dis[to],to});

}

}

}

}

bool spfa(int v){

vis[v] = 1;

queue<int> q;

for(int i = 0;i<=n;i++) dp[i] = inf;

dp[v] = 0;

q.push(v);

while(!q.empty()){

int u = q.front();

vis[u] = 0;

  q.pop();

for(int i = head[u];i;i=e[i].next){

int to = e[i].to,w = e[i].w;

if(dp[u]+w<dp[to]){

dp[to] = dp[u]+w;

if(!vis[to]){

vis[to] = 1;

q.push(to);

check_cir[to]++;

if(check_cir[to]>n) return 0;

}

}

}

}

return 1;

}

int main(){

ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

cin>>n>>m;

for(int i = 0;i<m;i++){

int x,y,w;

cin>>x>>y>>w;

add(x,y,w);

}

for(int i = 1;i<=n;i++){

add(0,i,0);

}

if(!spfa(0)) {

cout<<-1<<endl;

return 0;

}

else {

for(int i = 1;i<=n;i++){

for(int j = head[i];j;j=e[j].next){

int to = e[j].to;

e[j].w += dp[i] - dp[to];

}

} // 修改边权

for(int i = 1;i<=n;i++){

dijkstra(i);

ll ans = 0;

for(ll j = 1;j<=n;j++){

if(dis[j] == inf){

ans += j*inf;

}

else ans += j * (dis[j] + dp[j] - dp[i]);

}

cout<<ans<<endl;

}

}

return 0;

}

二分图最大匹配(最小点覆盖n==最大匹配数m)

(最大独立集=n-最小点覆盖=n-最大边匹配)

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

struct edge{

    int to,next;

}e[N];

int ecnt,head[N],n,k;

void add(int u,int v){

    e[++ecnt].to = v;

    e[ecnt].next = head[u];

    head[u] = ecnt;

}

int G[1000][1000],ans;

int dir[4][2] = {1,0,0,1,0,-1,-1,0};

int math[N],vis[21000];

bool dfs(int v){

    for(int i = head[v];i;i = e[i].next){

        int to = e[i].to;

        if(!vis[to]){

            vis[to] = 1;

            if(!math[to]||dfs(math[to])) {

                math[to] = v;

                return 1;

            }

        }

    }

    return 0;

}

int main(){

    cin>>n>>k;

    for(int i = 0;i<k;i++){

        int x,y;

        cin>>x>>y;

        G[x][y] = 1;

    }

    for(int i = 1;i<=n;i++){

        for(int j = 1;j<=n;j++){

            if((i+j)&1||G[i][j]) continue;

            for(int k = 0;k<4;k++){

                int x = dir[k][0] + i;

                int y = dir[k][1] + j;

                if(x>=1&&y>=1&&x<=n&&y<=n&&G[x][y]==0){

                    add((i-1)*n+j,(x-1)*n+y);

                }

            }

        }

    }

    for(int i = 1;i<=n;i++){

        for(int j = 1;j<=n;j++){

            if((i+j)&1) continue;

            memset(vis,0,sizeof(vis));

            if(dfs((i-1)*n+j)) ans++;

        }

    }

    cout<<ans<<endl;

    return 0;

}

最大流问题

EK算法

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

typedef long long ll;

int n,m,x;

struct edge{

int from,to,next,w;

}e[N];

int head[N],ecnt=0,A[N],fa[N];

void add(int u,int v,int w){

e[ecnt] = {u,v,head[u],w};

head[u] = ecnt++;

}

bool bfs(){

queue<int> q;

q.push(1);

for(int i = 0;i<=n;i++) A[i] = 0;

A[1] = 0x3f3f3f3f;

while(!q.empty()){

int u = q.front();

q.pop();

for(int i = head[u];i!=-1;i = e[i].next){

int to = e[i].to , w = e[i].w;

if(!A[to]&&e[i].w){

fa[to] = i;

A[to] = min(A[u],w);

q.push(to);

}

}

if(A[n]) return 1;

}

return 0;

}

ll EK(){

ll ans = 0;

while(bfs()){

for(int i = n;i!=1;i = e[fa[i]].from){

e[fa[i]].w -= A[n];

e[fa[i]^1].w += A[n];

}

ans += A[n];

}

return ans;

}

int main(){

ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

cin>>n>>m>>x;

for(int i = 0;i<4*m;i++){

head[i] = -1;

}

for(int i = 0;i<m;i++){

int a,b,c;

cin>>a>>b>>c;

add(a,b,c);

add(b,a,0);

}

ll ans = EK();

if(ans == 0) cout<<"Orz Ni Jinan Saint Cow!\n";

else {

cout<<ans<<" ";

ans = ceil(x*1.0/ans);

cout<<ans<<"\n";

}

return 0;

}

Dinic算法

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

typedef long long ll;

#define int long long

const int inf = 0x3f3f3f3f;

int n,m,s,t;

struct edge{

int to,next,now,w;

}e[N];

int head[N],ecnt,d[N],vis[N],cur[N];

void add(int u,int v,int w){

e[ecnt] = {v,head[u],0,w};

head[u] = ecnt++;

}

bool bfs(int s,int t){

for(int i = 0;i<=n;i++) d[i] = vis[i] = 0;

queue<int> q;

q.push(s);

vis[s] = 1;

while(!q.empty()){

int u = q.front();

q.pop();

for(int i = head[u];~i;i = e[i].next){

int to = e[i].to,now = e[i].now,w = e[i].w;

if(!vis[to]&&now < w){

vis[to] = 1;

d[to] = d[u] + 1;

q.push(to);

if(to == t) return 1;

}

}

}

return vis[t];

}

ll dfs(int u,int t,int imp){

if(u == t || !imp) return imp;

ll res = 0;

for(int &i = cur[u];~i;i=e[i].next){

int to = e[i].to;

int now = e[i].now;

if(d[u]+1!=d[to]) continue;

int f = dfs(to,t,min(imp,e[i].w-now)) ;

if(f==0) continue;

e[i].now += f;

e[i^1].now -= f;

res += f;

imp -= f;

if(!imp) break;

}

return res;

}

ll Dinic(){

ll flow = 0;

while(bfs(s,t)){

for(int i = 0;i<=m;i++){

cur[i] = head[i];

}

flow += dfs(s,t,inf);

}

return flow;

}

signed main(){

ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

cin>>n>>m>>s>>t;

for(int i = 0;i<=m;i++){

head[i] = -1;

}

for(int i = 0;i<m;i++){

int a,b,w;

cin>>a>>b>>w;

add(a,b,w);

add(b,a,0);

}

cout<<Dinic()<<endl;

return 0;

}

KM算法 二分图最大权匹配(n^3)

 #include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N=505;

const ll inf=1e18;

ll n,m,G[N][N],matched[N];

ll slack[N],pre[N],ex[N],ey[N];//ex,ey顶标

bool visx[N],visy[N];

ll A[N],P[N],C[N],B[N];

pair<ll,ll> pi[N];

void dfs(ll u)

{

    ll x,y=0,yy=0,delta;

    memset(pre,0,sizeof(pre));

    for(ll i=1;i<=n;i++)slack[i]=inf;

    matched[y]=u;

    while(1)

    {

        x=matched[y];delta=inf;visy[y]=1;

        for(ll i=1;i<=n;i++)

        {

            if(visy[i])continue;

            if(slack[i]>ex[x]+ey[i]-G[x][i])

            {

                slack[i]=ex[x]+ey[i]-G[x][i];

                pre[i]=y;

            }

            if(slack[i]<delta){delta=slack[i];yy=i;}

        }

        for(ll i=0;i<=n;i++)

        {

            if(visy[i])ex[matched[i]]-=delta,ey[i]+=delta;

            else slack[i]-=delta;

        }

        y=yy;

        if(matched[y]==-1)break;

    }

    while(y){matched[y]=matched[pre[y]];y=pre[y];}

}

ll KM()

{

    memset(matched,-1,sizeof(matched));

    memset(ex,0,sizeof(ex));

    memset(ey,0,sizeof(ey));

    for(ll i=1;i<=n;i++)

    {

        memset(visy,0,sizeof(visy));

        dfs(i);

    }

    ll res=0;

    for(ll i=1;i<=n;i++)

        if(matched[i]!=-1)res+=G[matched[i]][i];

    return res;

}

int main()

{

ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

cin>>n>>m;

    for(int i=1;i<=n;i++)

        for(int j=1;j<=n;j++)

            G[i][j]=-inf;

    for(int i = 1;i<=m;i++){

     ll a,b,c;

     cin>>a>>b>>c;

     G[a][b] = c;

}

    printf("%lld\n",KM());

    for(int i = 1;i<=n;i++){

     printf("%lld ",matched[i]);

}

    return 0;

}

// n 存的点的个数 ,KM()表示最大权匹配,mathched表示匹配结果

欧拉图

(1) 存在欧拉路径的充分必要条件: 度数为奇数的点只能是0或者2个(0个为欧拉回路的情况)

(2) 存在欧拉回路的充分必要条件: 度数为奇数的点只能有0个

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6+7;

int n,m,ver,use[N],din[N],dout[N],head[N],ecnt,dfn[N],low[N];

struct edge{

int next,to;

}e[N];

void add(int u,int v){

e[ecnt].to = v;

e[ecnt].next = head[u];

head[u] = ecnt++;

}

vector<int> ans;

void dfs(int u){

for(int &i = head[u];~i;){

if(use[i]) {

i = e[i].next;

continue;

}

use[i] = 1;

if(ver) use[i^1] = 1;

int t;

if(ver) {

t = i/2+1;

if(i&1) t = -t;

}

else t = i+1;

int to = e[i].to;

i = e[i].next;

dfs(to);

ans.push_back(t);

}

}

int main(){

cin>>ver>>n>>m;

if(ver==2) ver = 0;

memset(head,-1,sizeof head);

for(int i = 0;i<m;i++){

int a,b;

cin>>a>>b;

add(a,b);

if(ver) add(b,a);  

din[b]++;dout[a]++;

}

if(ver==1){

for(int i = 1;i<=n; i++){

if(din[i]+dout[i] &1){

cout<<"NO"<<endl;

return 0;

}

}

}

else for(int i = 1;i<=n;i++){

if(din[i] != dout[i]){

cout<<"NO"<<endl;

return 0;

}

}

for(int i = 1;i<=n;i++){

if(~head[i]){

dfs(i);

break;

}

}

if(ans.size()<m) cout<<"NO"<<endl;

else{

cout<<"YES"<<endl;

for(int i = (int)ans.size()-1;i>=0;i--){

cout<<ans[i]<<" ";

}

}

return 0;

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM 算法模板集 Contents 一. 常用函数与STL 二. 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 三. 大数模板,字符读入 四. 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 五. 图论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分图最大匹配(匈牙利算法) 12. 带权二分图最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向图割边割点和双连通分量 16. 最小树形图O(N^3) 17. 最小树形图O(VE) 六. 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点求圆心坐标 4. 三角形几个重要的点 七. 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符串匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分图网络最大流最小割 30. 混合图欧拉回路 31. 无源汇上下界网络流 32. 二分图最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法求函数波峰 35. 单词计数,矩阵乘法 36. 字符串和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基
看大小就知道很全啦 查看地址 https://blog.csdn.net/qq_43333395/article/details/98508424 目录: 数据结构: 1.RMQ (区间最值,区间出现最大次数,求区间gcd) 2.二维RMQ求区间最大值 (二维区间极值) 3.线段树模板(模板为区间加法) (线段树染色) (区间最小值) 4.线性基 (求异或第k大) 5.主席树(静态求区间第k小) (区间中小于k的数量和小于k的总和) (区间中第一个大于或等于k的值) 6.权值线段树 (求逆序对) 7.动态主席树 (主席树+树状数组) (区间第k大带修改) 8.树上启发式合并 (查询子树的优化) 9,树状数组模板 (求区间异或和,求逆序对) 扩展 10.区间不重复数字的和 (树状数组) 11.求k维空间中离所给点最近的m个点,并按顺序输出(KD树) 12.LCA (两个节点的公共父节点) 动态规划: 1.LIS (最长上升子序列) 2.有依赖的背包 (附属关系) 3.最长公共子序列(LCS) 4.树形DP 5.状压DP-斯坦纳树 6.背包 7.dp[i]=min(dp[i+1]…dp[i+k]),multset 博弈: 1.NIM博弈 (n堆每次最少取一个) 2.威佐夫博弈(两堆每次取至少一个或一起取一样的) 3.约瑟夫环 4.斐波那契博弈 (取的数依赖于对手刚才取的数) 5.sg函数 数论: 1.数论 素数检验:普通素数判别 线性筛 二次筛法求素数 米勒拉宾素数检验 2.拉格朗日乘子法(求有等式约束条件的极值) 3.裂项(多项式分子分母拆分) 4.扩展欧几里得 (ax+by=c) 5.勾股数 (直角三角形三边长) 6.斯特林公式 (n越大越准确,求n!) 7.牛顿迭代法 (求一元多次方程一个解) 8.同余定理 (a≡b(mod m)) 9.线性求所有逆元的方法求 (1~p modp的逆元) 10.中国剩余定理(n个同余方程x≡a1(modp1)) 11.二次剩余((ax+k)2≡n(modp)(ax+k)^2≡n(mod p)(ax+k) 2 ≡n(modp)) 12.十进制矩阵快速幂(n很大很大的时候) 13.欧拉函数 14.费马小定理 15.二阶常系数递推关系求解方法 (a_n=p*a_{n-1}+q*a_{n-2}) 16.高斯消元 17.矩阵快速幂 18.分解质因数 19.线性递推式BM(杜教) 20.线性一次方程组解的情况 21.求解行列式的逆矩阵,伴随矩阵,矩阵不全随机数不全 组合数学: 1.循环排列 (与环有关的排列组合) 计算几何: 1.三角形 (求面积)) 2.多边形 3.三点求圆心和半径 4.扫描线 (矩形覆盖求面积) (矩形覆盖求周长) 5.凸包 (平面上最远点对) 6.求凸多边形的直径 7.求凸多边形的宽度 8.求凸多边形的最小面积外接矩形 9.半平面交 图论: 基础:前向星 1.最短路(优先队列dijkstra) 2.判断环(tarjan算法) 3.最小生成树(Kruskal 模板) 4.最小生成树(Prim) 5.Dicnic最大流(最小割) 6.无向图最小环(floyd) 7.floyd算法动态规划(通过部分指定边的最短路) 8.图中找出两点间的最长距离 9.最短路 (spfa) 10.第k短路 (spfa+A*) 11.回文树模板 12.拓扑排序 (模板) 13.次小生成树 14.最小树形图(有向最小生成树) 15.并查集 (普通并查集,带权并查集,) 16.求两个节点的最近公共祖先 (LCA) 17.限制顶点度数的MST(k度限制生成树) 18.多源最短路(spfa,floyd) 19.最短路 (输出字典序最小) 20.最长路 图论题目简述 字符串: 1.字典树(多个字符串的前缀) 2.KMP(关键字搜索) 3.EXKMP(找到S中所有P的匹配) 4.马拉车(最长回文串) 5.寻找两个字符串的最长前后缀(KMP) 6.hash(进制hash,无错hash,多重hash,双hash) 7.后缀数组 (按字典序排字符串后缀) 8.前缀循环节(KMP的fail函数) 9.AC自动机 (n个kmp) 10.后缀自动机 小技巧: 1.关于int,double强转为string 2.输入输出挂 3.低精度加减乘除 4.一些组合数学公式 5.二维坐标的离散化 6.消除向下取整的方法 7.一些常用的数据结构 (STL) 8.Devc++的使用技巧 9.封装好的一维离散化 10.Ubuntu对拍程序 11.常数 12.Codeblocks使用技巧 13.java大数 叮嘱 共173页
1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值