最近补的算法

1. 高斯消元

求矩阵逆, 增广单位阵然后消元

求行列式, 先消元后对角线乘积即为行列式

https://codeforces.com/contest/832/submission/61763466  (取模)

https://codeforces.com/contest/1344/submission/84584146 (bitset优化)

https://codeforces.com/gym/102501/submission/87009936 (bitset优化)

void Gauss(int A[N][N], int n, int m, int q) {
    //A为n行m+q列矩阵, n为方程数, m为变量数
    //q为增广的列数, r为矩阵秩
    int r = 1;
    REP(i,1,m) {
        int p = r;
        while (!A[p][i]&&p<=n) ++p;
        if (p>n) continue;
        if (p!=r) REP(j,1,m+q) swap(A[p][j],A[r][j]);
        REP(j,1,n) if (j!=r&&A[j][i]) {
            ll t = A[j][i]*inv(A[r][i])%P;
            REP(k,1,m+q) A[j][k]=(A[j][k]-t*A[r][k]%P+P)%P;
        }
        ++r;
    }
}
View Code

 

2. exgcd

ll exgcd(ll a, ll b, ll &x, ll &y) {
    ll d = a;
    if (!b) return x=1,y=0,d;
    return d=exgcd(b,a%b,y,x),y-=a/b*x,d;
}
View Code

 

3. 欧拉回路

https://codeforces.com/contest/1152/submission/84629674 (输出点)

https://codeforces.com/contest/1361/submission/84628129 (输出边)

void dfs(int x) {
    while (g[x].size()) {
        auto e = g[x].back(); g[x].pop_back();
        if (!vis[e.y]) vis[e.y]=1, dfs(e.x);
    }
    r.push_back(x);
}
View Code

 

4. 平面图最大流

源点与汇点连一条无穷边, 转为对偶图最短路问题

//https://www.luogu.com.cn/record/35131137
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 2e6+10;
const int S=N-1, T=N-2;
int n,m,vis[N];
ll d[N];
vector<pii> g[N];
struct node {
    int x;
    ll w;
    bool operator < (const node &rhs) const {
        return w>rhs.w;
    }
};
priority_queue<node> q;
int ID(int x, int y, int tp) {
    return (x-1)*(m-1)+y+tp*(n-1)*(m-1);
}
void add(int u, int v, int w) { 
    g[u].pb({v,w}),g[v].pb({u,w});
}
void dij() {
    REP(i,1,(n-1)*(m-1)*2) d[i]=1e18,vis[i]=0;
    d[S]=d[T]=1e18,vis[S]=vis[T]=0;
    q.push({S,d[S]=0});
    while (q.size()) {
        int x = q.top().x; q.pop();
        if (vis[x]) continue;
        vis[x] = 1;
        for (auto &e:g[x]) {
            ll dd = e.y+d[x];
            if (d[e.x]>dd) q.push({e.x,d[e.x]=dd});
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    REP(i,1,n) REP(j,1,m-1) {
        int x;
        scanf("%d", &x);
        if (i==1) add(ID(1,j,0),S,x);
        else if (i==n) add(ID(n-1,j,1),T,x);
        else add(ID(i-1,j,1),ID(i,j,0),x);
    }
    REP(i,1,n-1) REP(j,1,m) {
        int x;
        scanf("%d", &x);
        if (j==1) add(ID(i,1,1),T,x);
        else if (j==m) add(ID(i,m-1,0),S,x);
        else add(ID(i,j-1,0),ID(i,j,1),x);
    }
    REP(i,1,n-1) REP(j,1,m-1) {
        int x;
        scanf("%d", &x);
        add(ID(i,j,0),ID(i,j,1),x);
    }
    dij();
    printf("%lld\n", d[T]);
}

luogu4001
luogu4001
//https://ac.nowcoder.com/acm/contest/view-submission?submissionId=44220389
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head
 
 
 
const int N = 2e6+10;
const int S=N-1, T=N-2;
int n,m,vis[N];
ll d[N];
vector<pii> g[N];
struct node {
    int x;
    ll w;
    bool operator < (const node &rhs) const {
        return w>rhs.w;
    }
};
priority_queue<node> q;
int ID(int x, int y) {
    return (x-1)*n+y;
}
void add(int u, int v, int w) {
    d[u]=d[v]=1e18;
    g[u].pb({v,w}),g[v].pb({u,w});
}
void dij() {
    d[T]=1e18;
    q.push({S,d[S]=0});
    while (q.size()) {
        int x = q.top().x; q.pop();
        if (vis[x]) continue;
        vis[x] = 1;
        for (auto &e:g[x]) {
            ll dd = e.y+d[x];
            if (d[e.x]>dd) q.push({e.x,d[e.x]=dd});
        }
    }
}
int main() {
    scanf("%d%d", &n, &m);
    REP(i,1,m) {
        int l,r,c;
        char dir;
        scanf("%d%d %c%d", &l, &r, &dir, &c);
        if (dir=='R') {
            if (l==1) add(S,ID(l,r),c);
            else add(ID(l-1,r),ID(l,r),c);
        }
        else {
            if (r==n) add(ID(l,r),T,c);
            else add(ID(l,r),ID(l,r+1),c);
        }
    }
    dij();
    printf("%lld\n", d[T]==1e18?-1:d[T]);
}

2020牛客2I
2020牛客2I

 

5. Pollard_Rho

//https://nanti.jisuanke.com/t/42544
#include <bits/stdc++.h>
#define ctz __builtin_ctzll
typedef long long ll;
using namespace std;

mt19937 rd(time(0));
const int p[12]={2,3,5,7,11,13,17,19,23,29,31,37};
const int p_cnt=12;

ll mmul(ll a,ll b,ll m) { //a*b%m
    ll d=((long double)a/m*b+1e-8);
    ll r=a*b-d*m;
    return r<0?r+m:r;
}
ll FastPow(ll a,ll b,ll m) {
    ll ans=1;
    for (;b;b>>=1,a=mmul(a,a,m))
        if (b&1) ans=mmul(ans,a,m);
    return ans;
}
ll gcd(ll a,ll b) {
    if (!a||!b) return a+b;
    int t=ctz(a|b); a>>=ctz(a);
    do {
        b>>=ctz(b);
        if (a>b) swap(a,b);
        b-=a;
    } while (b);
    return a<<t;
}

int isprime(ll n) {
    if (n==1) return 0;
    if (n==2||n==3||n==5) return 1;
    if (!(n&1)||!(n%3)||!(n%5)) return 0;

    ll m=n-1; int k=0;
    while (!(m&1)) m>>=1,++k;
    for (int ip=0;ip<p_cnt&&p[ip]<n;++ip) {
        ll x=FastPow(p[ip],m,n),y=x;
        for (int i=0;i<k;++i) {
            x=mmul(x,x,n);
            if (x==1&&y!=1&&y!=n-1) return 0;
            y=x;
        }
        if (x!=1) return 0;
    }
    return 1;
}

ll f[110];
int cnt;

ll g(ll x,ll n,ll a) {
    ll t=mmul(x,x,n)+a;
    return t<n?t:t-n;
}

const int M=(1<<7)-1;
ll Pollard_Rho(ll n) {
    if (!(n&1)) return 2;
    if (!(n%3)) return 3;
    if (!(n%5)) return 5;

    ll x=0,y=x,t=1,q=1,a=(rd()%(n-1))+1;
    for (int k=2;;k<<=1,y=x,q=1) {
        for (int i=1;i<=k;++i) {
            x=g(x,n,a);
            q=mmul(q,abs(x-y),n);
            if (!(i&M)) {
                t=gcd(q,n);
                if (t>1) break;
            }
        }
        if (t>1||(t=gcd(q,n))>1) break;
    }
    if (t==n) {
        t=1;
        while (t==1) t=gcd(abs((x=g(x,n,a))-y),n);
    }
    return t;
}

void work(ll n) {
    if (n==1) return;
    if (isprime(n)) f[++cnt]=n;
    else {
        ll t=n;
        while (t==n) t=Pollard_Rho(n);
        work(t),work(n/t);
    }
}


const int N = 1e5+10;
int n;
ll a[N],x,y;
ll calc(ll x, ll p) {
    ll ans = 0;
    while (x>=p) ans+=x/p,x/=p;
    return ans;
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%lld%lld", &n, &x, &y);
        for(int i=1;i<=n;++i) scanf("%lld", a+i);
        cnt = 0;
        work(x);
        int top = 0;
        ll ans = 1e18;
        for(int i=1;i<=cnt;++i) if (x%f[i]==0) {
            int tx = 0;
            while (x%f[i]==0) x/=f[i],++tx;
            ll ty = calc(y,f[i]);
            ll tz = 0;
            for(int j=1;j<=n;++j) tz += calc(a[j],f[i]);
            ans = min(ans, (ty-tz)/tx);
        }
        printf("%lld\n", ans);
    }
}
2019xuzhouE

 

6. 换根dp

核心思路是正反两次dp统计答案

//http://acm.hdu.edu.cn/showproblem.php?pid=6662
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
const int N = 1e5+10;
const ll INF = 1e18;
int n, a[N], fa[N];
vector<int> g[N];
struct {
    void add_min(ll x, int id) {
        if (x==INF) return;
        if (!a[0].second) a[0] = {x,id};
        else if (!a[1].second) a[1] = {x,id};
        else {
            if (a[0].second==id) a[0].first = min(a[0].first, x);
            else if (a[1].first>x) a[1] = {x,id};
        }
        if (a[0].second&&a[1].second&&a[0].first>a[1].first) swap(a[0],a[1]);
    }
    void add_max(ll x, int id) {
        if (x==INF) return;
        if (!a[0].second) a[0] = {x,id};
        else if (!a[1].second) a[1] = {x,id};
        else {
            if (a[0].second==id) a[0].first = max(a[0].first, x);
            else if (a[1].first<x) a[1] = {x,id};
        }
        if (a[0].second&&a[1].second&&a[0].first<a[1].first) swap(a[0],a[1]);
    }
    void add_min(ll x, int y, int id) {
        if (x==INF) add_min(y,id);
        else add_min(x+y,id);
    }
    void add_max(ll x, int y, int id) {
        if (x==INF) add_max(y,id);
        else add_max(x+y,id);
    }
    ll ask(int id) {
        if (a[0].second!=id) return a[0].first;
        return a[1].first;
    }
    pair<ll,int> a[2] = {{INF,0},{INF,0}};
} mi[N],ma[N];
void dfs(int x, int f) {
    fa[x] = f;
    for (int y:g[x]) if (y!=f) {
        dfs(y,x);
        mi[x].add_min(ma[y].ask(-1), a[y], y);
        ma[x].add_max(mi[y].ask(-1), a[y], y);
    }
}
void dfs2(int x, int f) {
    for (int y:g[x]) if (y!=f) { 
        mi[y].add_min(ma[x].ask(y), a[x], x);
        ma[y].add_max(mi[x].ask(y), a[x], x);
        dfs2(y,x);
    }
}
void work() {
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) {
        scanf("%d", a+i);
        g[i].clear();
        mi[i] = ma[i] = {};
    }
    for (int i=1; i<=n; ++i) {
        int t;
        scanf("%d", &t);
        a[i] -= t;
    }
    if (n==1) return printf("%d\n", a[1]),void();
    for (int i=1; i<n; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v),g[v].push_back(u);
    }
    dfs(1,0),dfs2(1,0);
    ll ans = -INF;
    for (int i=1; i<=n; ++i) ans = max(ans, mi[i].ask(-1)+a[i]);
    printf("%lld\n", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) work();
}
hdu6662维护最大次大

 

7. 点分树

先点分治记录每一棵树, 那么每次询问对于点$x$的贡献, 遍历$x$所在的$O(\log{n})$棵树, 考虑$lca$为当前树根的情况即可

//https://ac.nowcoder.com/acm/contest/view-submission?submissionId=44505218
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head


//belong[x][y] 表示x在第i层属于哪个子树
//cen[x][y] 表示x在第y层所在树的根
const int N = 5e4+10;
int n,m, sum, rt, vis[N], sz[N], mx[N];
ll tag[N], tot, ans[N], ans2[N][20];
int belong[N][20], cen[N][20], dep[N][20], cnt[N], cnt2[N][20], cnt3[N];
vector<int> g[N];
void getrt(int x, int f) {
    mx[x] = 0, sz[x] = 1;
    for (int y:g[x]) if (y!=f&&!vis[y]) { 
        getrt(y,x),sz[x]+=sz[y];
        mx[x] = max(mx[x], sz[y]);
    }
    mx[x] = max(mx[x], sum-sz[x]);
    if (mx[x]<mx[rt]) rt = x;
}
void dfs(int x, int f, int d, int dd, int f1, int f2) {
    belong[x][dd] = f1, cen[x][dd] = f2, dep[x][dd] = d;
    for (int y:g[x]) if (y!=f&&!vis[y]) dfs(y,x,d+1,dd,f1,f2);
}
void dfs(int x, int f, int d) {
    vis[x] = 1, cen[x][d] = x;
    for (int y:g[x]) if (!vis[y]) dfs(y,x,1,d,y,x);
    for (int y:g[x]) if (!vis[y]) {
        mx[rt=0]=n, sum=sz[y];
        getrt(y,0), dfs(rt,x,d+1);
    }
}
void upd(int x, int w) { 
    tot += w;
    int d = 0;
    ++cnt3[x];
    while (cen[x][d]!=x) {
        ans[cen[x][d]] += dep[x][d];
        ans2[belong[x][d]][d] += dep[x][d];
        ++cnt[cen[x][d]], ++cnt2[belong[x][d]][d];
        ++d;
    }
}
ll qry(int x) {
    ll now = tot+tag[x]-ans[x];
    int d = 0;
    while (cen[x][d]!=x) {
        int c = cen[x][d], b = belong[x][d];
        now -= ans[c]-ans2[b][d]+(ll)dep[x][d]*(cnt[c]-cnt2[b][d])+(ll)cnt3[c]*dep[x][d];
        ++d;
    }
    return now;
}
void work() {
    scanf("%d%d", &n, &m);
    tot = 0;
    REP(i,1,n) { 
        g[i].clear();
        vis[i] = tag[i] = ans[i] = cnt[i] = cnt3[i] = 0;
        REP(j,0,19) ans2[i][j] = cnt2[i][j] = 0;
    }
    REP(i,2,n) {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].pb(v),g[v].pb(u);
    }
    sum=mx[rt=0]=n,getrt(1,0);
    dfs(rt,0,0);
    while (m--) {
        int op, x, w;
        scanf("%d%d", &op, &x);
        if (op==1) scanf("%d", &w), upd(x,w);
        else if (op==2) {
            ll val = qry(x);
            if (val>0) tag[x] -= val;
        }
        else printf("%lld\n", qry(x));
    }
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) work();
}
2020牛客7C

 

8. 子集卷积

//http://acm.hdu.edu.cn/showproblem.php?pid=6851
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int*,int> poly;
const int P = 998244353;
poly A[21], B[21], C[21];
int cnt[1<<21];
void add(int &x, ll y) {x=(x+y)%P;}
void FMT(poly a, int tp) {
    int len = 1<<a.y;
    for (int i=1; i<len; i<<=1) {
        for (int j=0; j<len; j+=i<<1) {
            for (int k=j; k<j+i; ++k) add(a.x[k+i], tp*a.x[k]);
        }
    }
}
//求a和b的子集卷积
poly mul(poly a, poly b) {
    int len = 1<<a.y;
    for (int i=0; i<=a.y; ++i) {
        A[i].x = new int[len]();
        B[i].x = new int[len]();
        C[i].x = new int[len]();
        A[i].y = B[i].y = C[i].y = a.y;
    }
    for (int i=0; i<len; ++i) { 
        A[cnt[i]].x[i] = a.x[i];
        B[cnt[i]].x[i] = b.x[i];
    }
    for (int i=0; i<=a.y; ++i) {
        FMT(A[i], 1), FMT(B[i], 1);
        for (int x=0; x<len; ++x) {
            for (int j=0; j<=i; ++j) {
                add(C[i].x[x], (ll)A[j].x[x]*B[i-j].x[x]);
            }
        }
        FMT(C[i],-1);
    }
    poly ans(new int[len], a.y);
    for (int i=0; i<len; ++i) ans.x[i] = C[cnt[i]].x[i];
    for (int i=0; i<=a.y; ++i) delete A[i].x, delete B[i].x, delete C[i].x;
    return ans;
}

poly solve(poly p) {
    poly ans(new int[1<<21], 21);
    ans.x[0] = 1;
    for (int i=0; i<p.y; ++i) {
        poly L(new int[1<<i], i), R(new int[1<<i], i);
        for (int j=0; j<(1<<i); ++j) L.x[j] = ans.x[j];
        for (int j=1<<i; j<(1<<i+1); ++j) R.x[j^1<<i] = p.x[j];
        L = mul(L, R);
        for (int j=0; j<(1<<i); ++j) ans.x[j^1<<i] = (L.x[j]+P)%P;
        delete L.x, delete R.x;
    }
    return ans;
}

int main() {
    for (int i=1; i<(1<<21); ++i) cnt[i] = cnt[i>>1]+(i&1);
    int n;
    scanf("%d", &n);
    poly f(new int[1<<21](), 21);
    while (n--) {
        int p, b;
        scanf("%d%d", &p, &b);
        f.x[b] = (f.x[b]+p)%P;
    }
    f = solve(f);
    int q;
    scanf("%d", &q);
    while (q--) {
        int x;
        scanf("%d", &x);
        printf("%d\n", f.x[x]);
    }
}
hdu6851

 

9. 数位dp

求满足条件的$(i,j)$对数, 只要考虑$dp_{n,0/1,0/1}$表示遍历到第$n$位$i,j$是否有限制即可

//https://ac.nowcoder.com/acm/contest/5671/H
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7;
const int N = 110, M = 1111;
char s[N];
int n,dp[2][1111*2+10][2][2];
void add(int &a, int b) {a+=b;if (a>=P)a-=P;}
int main() {
    scanf("%s", s+1);
    n = strlen(s+1);
    for (int i=1; i<=n; ++i) s[i] -= '0';
    reverse(s+1,s+1+n);
    int cur = 0, mx = 0, ans = 0;
    dp[0][M][1][1] = 1;
    ll tot = 0;
    for (int i=n; i>=0; --i) {
        cur ^= 1;
        for (int x=-mx-9; x<=mx+9; ++x) {
            dp[cur][x+M][0][0]=dp[cur][x+M][0][1]=dp[cur][x+M][1][0]=dp[cur][x+M][1][1]=0;
        }
        for (int x=-mx; x<=mx; ++x) {
            for (auto a1:{0,1}) for (auto a2:{0,1}) {
                int &r = dp[cur^1][x+M][a1][a2];
                if (!r) continue;
                if (!i) {if (x>0) add(ans,r);continue;}
                int m2 = a2?s[i]:9;
                for (int v=0; v<=m2; ++v) {
                    int m1 = a1?v:9;
                    for (int u=0; u<=m1; ++u) {
                        add(dp[cur][x+u-v+M][a1&&u==m1][a2&&v==m2],r);
                    }
                }
            }
        }
        mx += 9;
    }
    if (ans<0) ans += P;
    printf("%d\n", ans);
}
2020牛客6H 

求$[L,R]$满足条件的数的贡献, 只要枚举第一个无限制的位置硬算即可

//https://codeforces.com/contest/1073/problem/E
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 998244353;
const int N = 20, B = 11;
int k, pw[B][N], b[B][N];
int a[B],vis[B],f[N][B][B],sum[N];

int solve(int n, int x) {
    int cnt = 0;
    for (int i=0; i<=9; ++i) if (vis[i]) ++cnt;
    if (cnt>k) return 0;
    if (!n) return x;
    int ans = (ll)x*f[n][cnt][k-cnt]%P;
    for (int i=1; i<=9; ++i) {
        if (!vis[i]) ++cnt;
        if (cnt<=k) ans = (ans+(ll)b[i][n]*f[n-1][cnt][k-cnt])%P;
        if (!vis[i]) --cnt;
    }
    return ans;
}

int calc(ll x) {
    for (int i=0; i<=9; ++i) vis[i] = 0;
    int cnt = 0, n = 0, ans = 0;
    while (x) a[++n] = x%10, x/=10;
    for (int i=n; i; --i) {
        for (int j=i==n; j<a[i]; ++j) {
            ++vis[j];
            ans = (ans+solve(i-1,(x+(ll)j*pw[10][i-1])%P))%P;
            --vis[j];
        }
        if (++vis[a[i]]==1) ++cnt;
        x = (x+(ll)a[i]*pw[10][i-1])%P;
        if (cnt>k) break;
    }
    if (cnt<=k) ans = (ans+x)%P;
    return (ans+sum[n-1])%P;
}

int main() {
    ll l, r;
    scanf("%lld%lld%d", &l, &r, &k);
    for (int i=1; i<B; ++i) {
        pw[i][0] = 1;
        for (int j=1; j<N; ++j) {
            pw[i][j] = (ll)pw[i][j-1]*i%P;
            b[i][j] = (b[i][j-1]*10ll+i)%P;
        }
    }
    for (int n=0; n<N; ++n) for (int x=0; x<=k; ++x) for (int y=0; y<=min(n,k-x); ++y) {
        if (!y) f[n][x][y] = pw[x][n];
        else f[n][x][y] = ((ll)(10-x)*f[n-1][x+1][y-1]+(ll)x*f[n-1][x][y])%P;
    }
    for (int n=0; n<N; ++n) for (int x=0; x<=k; ++x) for (int y=1; y<=k-x; ++y) {
        f[n][x][y] = (f[n][x][y]+f[n][x][y-1])%P;
    }
    for (int i=1; i<N; ++i) {
        sum[i] = sum[i-1];
        for (int j=1; j<=9; ++j) {
            vis[j]=1,sum[i]=(sum[i]+solve(i-1,(ll)j*pw[10][i-1]%P))%P,vis[j]=0;
        }
    }
    int ans = calc(r);
    if (l!=1) ans = (ans-calc(l-1))%P;
    if (ans<0) ans += P;
    printf("%d\n", ans);
}
cf1073E

 

10. crt

$k$个同余方程$x\equiv a_i (mod \space m_i)$, $m_i$两两互质

设$M=\prod m_i, M_i=M/m_i, t_i=M_i^{-1} \space mod \space m_i$

解为$x\equiv \sum a_iM_it_i (mod \space M)$

 

11. KM

struct KM {
    int n,w[N][N],pre[N],visy[N],link[N],slack[N],lx[N],ly[N];
    void bfs(int k) {
        int x,y=0,yy=0,delta;
        for(int i=0;i<=n;++i) visy[i]=pre[i]=0,slack[i]=INF;
        link[y] = k;
        while (1) {
            x=link[y],delta=INF,visy[y]=1;
            for(int i=1;i<=n;++i) {
                if (!visy[i]) {
                    if (slack[i]>lx[x]+ly[i]-w[x][i]) {
                        slack[i]=lx[x]+ly[i]-w[x][i];
                        pre[i]=y;
                    }
                    if (slack[i]<delta) delta=slack[i],yy=i;
                }
            }
            for(int i=0;i<=n;++i) {
                if (visy[i]) lx[link[i]]-=delta,ly[i]+=delta;
                else slack[i]-=delta;
            }
            y=yy;
            if (link[y]==-1) break;
        }
        while (y) link[y]=link[pre[y]],y=pre[y];
    }
    ll solve(int _n) {
        n = _n;
        for(int i=0;i<=n;++i) link[i]=-1,lx[i]=ly[i]=0;
        for(int i=1;i<=n;++i) bfs(i);
        ll ans = 0;
        for(int i=1;i<=n;++i) ans+=w[link[i]][i];
        return ans;
    }
} km;
View Code

 

12. 高精度

//https://nanti.jisuanke.com/t/A1535
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int len[200];
char A[200],B[200],pw[200][55];
int main() {
    pw[0][1] = len[0] = 1;
    for (int i=1; i<=170; ++i) {
        for (int j=1; j<=len[i-1]; ++j) pw[i][j] = pw[i-1][j]*2;
        len[i] = 51;
        for (int j=1; j<=len[i]; ++j) pw[i][j+1] += pw[i][j]/10, pw[i][j]%=10;
        while (!pw[i][len[i]]) --len[i];
    }
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%s", A+1);
        int n = strlen(A+1), cnt = 0;
        reverse(A+1,A+1+n);
        for (int i=1; i<=n; ++i) A[i] -= '0';
        while (n) {
            B[++cnt] = A[1]&1;
            for (int k=n,c=0; k; --k) {
                int t = c*10+A[k];
                A[k] = t>>1;
                c = t&1;
            }
            while (n&&!A[n]) --n;
        }
        --cnt;
        for (int i=len[cnt]; i; --i) putchar(pw[cnt][i]+'0');
        puts("");
    }
}
2017南宁F

 

13. 费用流

atcoder上dijkstra优化费用流板子, 不支持添加负边

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) + (shortest(s, v) + dual[s] - dual[v])
                //         = - shortest(s, t) + dual[t] + shortest(s, v)
                //         = shortest(s, v) - shortest(s, t) >= 0 - (n-1)C
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};
View Code

支持添负边的版本, 跑的会慢一点 

template <class Cap, class Cost> struct mcf_graph {
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}
    int add_edge(int from, int to, Cap cap, Cost cost) {
        int m = pos.size();
        pos.push_back({from, int(g[from].size())});
        int from_id = g[from].size();
        int to_id = g[to].size();
        if (from==to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }
    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return {pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) result[i] = get_edge(i);
        return result;
    }
    std::pair<Cap,Cost> flow(int s, int t, Cap flow_limit) {
        std::vector<Cost> H(_n, 0), dis(_n);
        std::vector<int> pv(_n), pe(_n);
        struct Q {
            Cost key;
            int to;
            bool operator < (Q r) const {return key>r.key;}
        };
        Cap flow = 0;
        Cost cost = 0;
        while (flow<flow_limit) {
            std::priority_queue<Q> q;
            std::fill(dis.begin(),dis.end(),INF);
            q.push(Q{dis[s]=0, s});
            while (!q.empty()) {
                Q u = q.top(); q.pop();
                int v = u.to;
                if (dis[v]<u.key) continue;
                for (int i=0; i<g[v].size(); ++i) {
                    auto & e = g[v][i];
                    if (!e.cap) continue;
                    Cost cost = e.cost-H[e.to]+H[v];
                    if (dis[e.to]-dis[v]>cost) {
                        dis[e.to] = dis[v]+cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        q.push(Q{dis[e.to],e.to});
                    }
                }
            }
            if (dis[t]==INF) break;
            for (int i=0; i<_n; ++i) H[i] += dis[i];
            Cap c = flow_limit-flow;
            for (int v=t; v!=s; v=pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            flow += c;
            cost += c*H[t];
            for (int v=t; v!=s; v=pv[v]) {
                auto &e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
        }
        return {flow,cost};
    }
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge> > g;
};
View Code

https://atcoder.jp/contests/acl1/submissions/17011546

 

14. min-max容斥

$max(S)=\sum\limits_{T\subseteq S} (-1)^{|T|-1} min(T)$

//https://acm.ecnu.edu.cn/contest/317/problem/D/
#include <stdio.h>
const int P = 998244353;
int n, a[22], inv[2010];
int dp[2010][2010];
//n个礼物, 求搜集到每种时搜集次数期望
//设X_i为搜集到第i种搜集到的礼物数
//ans = max(X_i)
//dp[i][j] = i个白球, j个黑球, 第一次取出白球时期望次数
int main() { 
    scanf("%d", &n);
    int sum = 0;
    for (int i=0; i<n; ++i) scanf("%d", a+i),sum+=a[i];
    inv[1] = dp[1][0] = 1;
    for (int i=2; i<=sum; ++i) { 
        inv[i] = (long long)inv[P%i]*(P-P/i)%P;
        dp[i][0] = 1;
    }
    for (int i=1; i<=sum; ++i) for (int j=1; i+j<=sum; ++j) {
        dp[i][j] = ((long long)j*inv[i+j]%P*dp[i][j-1]+1)%P;
    }
    int mx = (1<<n)-1, ans = 0;
    for (int i=1; i<=mx; ++i) {
        int cur = -1, w = 0;
        for (int j=0; j<n; ++j) if (i>>j&1) { 
            cur = -cur;
            w = w+a[j];
        }
        ans = (ans+cur*dp[w][sum-w])%P;
    }
    if (ans<0) ans += P;
    printf("%d\n", ans);
}
View Code

 

15. lct

学习自https://www.cnblogs.com/flashhu/p/8324551.html

$lct$就是用多个$splay$维护一个森林, 每个$splay$维护森林中的一条链, 满足$3$条性质

  • splay中序遍历得到的点在原树中深度严格递增
  • 每个节点包含且仅包含于一个$splay$中
  • 一个$splay$的根的父亲指向这个$splay$维护的链中深度最小的点在原树中的父亲

要注意维护点权或者$sz$时一定要先$splay(x)$, 然后再修改

//luogu P2147 洞穴勘测
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
struct {
    int ch[2],fa,tag;
    void rev() {tag^=1;swap(ch[0],ch[1]);}
} tr[N];
//下推翻转标记
void pd(int o) {
    if (tr[o].tag) {
        tr[tr[o].ch[0]].rev();
        tr[tr[o].ch[1]].rev();
        tr[o].tag = 0;
    }
}
////向上维护信息, 这题不需要pushup
void pu(int o) {

}
//判断x是否是x所在splay的根
int nroot(int x) {
    return tr[tr[x].fa].ch[0]==x||tr[tr[x].fa].ch[1]==x;
}
////旋转操作跟普通splay稍不同, 要判断nroot(y)
void rot(int x) {
    int y=tr[x].fa,z=tr[y].fa,k=tr[y].ch[1]==x,w=tr[x].ch[!k];
    if (nroot(y)) tr[z].ch[tr[z].ch[1]==y]=x;
    tr[x].ch[!k]=y,tr[y].ch[k]=w;
    if (w) tr[w].fa=y;
    tr[y].fa=x,tr[x].fa=z;
    pu(x),pu(y);
}
void repush(int x) {
    if (nroot(x)) repush(tr[x].fa);
    pd(x);
}
//把x旋转到x所在splay的根, 标记一定要从根开始下传, 跟普通splay不同
void splay(int x) {
    repush(x);
    while (nroot(x)) {
        int y=tr[x].fa,z=tr[y].fa;
        if (nroot(y)) rot((tr[y].ch[0]==x)==(tr[z].ch[0]==y)?y:x);
        rot(x);
    }
    pu(x);
}
//把x与x所在原树的根之间的路径提出来, 用一个splay维护
void access(int x) {
    for (int y=0; x; x=tr[y=x].fa) splay(x),tr[x].ch[1]=y,pu(x);
}
//把x所在原树的根换为x, 并把x旋转到x所在splay的根
void makeroot(int x) {
    access(x),splay(x);
    tr[x].rev();
}
//找x所在原树的根, 并把x旋转到x所在splay的根
int findroot(int x) {
    access(x),splay(x);
    while (tr[x].ch[0]) pd(x),x=tr[x].ch[0];
    return splay(x), x;
}
//把x和y所在原树的路径提出来, 用一个splay维护, 并把y旋转到splay的根
//需要保证x和y在同一棵原树
void split(int x, int y) {
    makeroot(x),access(y),splay(y);
}
//连一条边(x,y)
void link(int x, int y) {
    makeroot(x);
    tr[x].fa=y;
}
//切掉(x,y)之间的边
void cut(int x, int y) {
    split(x,y);
    tr[y].ch[0]=tr[tr[y].ch[0]].fa=0;
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    while (m--) {
        char s[10];
        int x, y;
        scanf("%s%d%d", s, &x, &y);
        if (s[0]=='Q') puts(findroot(x)==findroot(y)?"Yes":"No");
        else if (s[0]=='C') link(x,y);
        else cut(x,y);
    }
}
维护森林连通性
//luogu P3690
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N];
struct {
    int ch[2],fa,tag,v;
    void rev() {tag^=1;swap(ch[0],ch[1]);}
} tr[N];
void pd(int o) {
    if (tr[o].tag) {
        tr[tr[o].ch[0]].rev();
        tr[tr[o].ch[1]].rev();
        tr[o].tag = 0;
    }
}
void pu(int o) {
    tr[o].v = tr[tr[o].ch[0]].v^tr[tr[o].ch[1]].v^a[o];
}
int nroot(int x) {
    return tr[tr[x].fa].ch[0]==x||tr[tr[x].fa].ch[1]==x;
}
void rot(int x) {
    int y=tr[x].fa,z=tr[y].fa,k=tr[y].ch[1]==x,w=tr[x].ch[!k];
    if (nroot(y)) tr[z].ch[tr[z].ch[1]==y]=x;
    tr[x].ch[!k]=y,tr[y].ch[k]=w;
    if (w) tr[w].fa=y;
    tr[y].fa=x,tr[x].fa=z;
    pu(x),pu(y);
}
void repush(int x) {
    if (nroot(x)) repush(tr[x].fa);
    pd(x);
}
void splay(int x) {
    repush(x);
    while (nroot(x)) {
        int y=tr[x].fa,z=tr[y].fa;
        if (nroot(y)) rot((tr[y].ch[0]==x)==(tr[z].ch[0]==y)?y:x);
        rot(x);
    }
    pu(x);
}
void access(int x) {
    for (int y=0; x; x=tr[y=x].fa) splay(x),tr[x].ch[1]=y,pu(x);
}
void makeroot(int x) {
    access(x),splay(x);
    tr[x].rev();
}
int findroot(int x) {
    access(x),splay(x);
    while (tr[x].ch[0]) pd(x),x=tr[x].ch[0];
    return splay(x), x;
}
void split(int x, int y) {
    makeroot(x),access(y),splay(y);
}
void link(int x, int y) {
    makeroot(x);
    if (findroot(y)!=x) tr[x].fa=y;
}
void cut(int x, int y) {
    makeroot(x);
    if (findroot(y)==x&&tr[y].fa==x&&!tr[y].ch[0]) {
        tr[y].fa=tr[x].ch[1]=0;
        pu(x);
    }
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
    while (m--) {
        int op, x, y;
        scanf("%d%d%d", &op, &x, &y);
        if (op==0) split(x,y),printf("%d\n", tr[y].v);
        else if (op==1) link(x,y);
        else if (op==2) cut(x,y);
        else splay(x),a[x]=y;
    }
}
维护森林路径异或和

 

16. 无向图求边双

//https://codeforces.com/gym/101480/submission/95483209
#include <bits/stdc++.h>
using namespace std;
const int P = 876756319, B = 99991;
const int N = 4550;
int n, m, u[N], v[N], vis[N], del[N], f[N];
int clk, cnt, dfn[N], low[N], q[N], bcc[N], has[N];
vector<pair<int,int>> g[N];

void dfs(int x, int fa, int tf) {
    f[x] = tf;
    dfn[x] = low[x] = ++clk;
    q[++*q] = x;
    for (auto &e:g[x]) {
        int y = e.first, id = e.second;
        if (del[id]||y==fa) continue;
        if (dfn[y]) low[x] = min(low[x], dfn[y]);
        else {
            dfs(y,x,tf);
            low[x] = min(low[x], low[y]);
        }
    }
    if (low[x]==dfn[x]) {
        ++cnt;
        do bcc[q[*q]] = cnt; while (q[(*q)--]!=x);
    }
}

void work() {
    *q = clk = cnt = 0;
    for (int i=1; i<=n; ++i) dfn[i] = low[i] = bcc[i] = 0;
    for (int i=1; i<=n; ++i) if (!dfn[i]) dfs(i,0,i);
    for (int i=1; i<=n; ++i) has[i] = ((int64_t)has[i]*B+bcc[i])%P;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i=0; i<m; ++i) {
        scanf("%d%d", &u[i], &v[i]);
        g[u[i]].push_back({v[i],i});
        g[v[i]].push_back({u[i],i});
    }
    for (int i=1; i<=m; ++i) {
        del[i] = 1;
        work();
        del[i] = 0;
    }
    work();
    int ans = 0;
    for (int i=1; i<=n; ++i) { 
        for (int j=i+1; j<=n; ++j) if (f[i]==f[j]) {
            if (bcc[i]!=bcc[j]) ++ans;
            else if (has[i]!=has[j]) ans += 2;
            else ans += 3;
        }
    }
    printf("%d\n", ans);
}
View Code

 

极角排序

splay

一般图匹配

 

posted @ 2020-06-25 01:04  dz8gk0j  阅读(183)  评论(0编辑  收藏  举报