最近补的算法
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; } }
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; }
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); }
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
//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
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); } }
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(); }
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(); }
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]); } }
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); }
求$[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); }
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;
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(""); } }
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; };
支持添负边的版本, 跑的会慢一点
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; };
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); }
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); }
极角排序
splay
一般图匹配