ACM板子大公开!

目前只有非常少的一部分,正在逐渐完善中...

基础

归并排序求逆序对

int merge_sort (int l, int r) {
    if (l >= r)    return 0;
    int mid = l + r >> 1;
    int res = merge_sort (l, mid) + merge_sort (mid + 1, r);

    int k = 0, i = l, j = mid + 1; //计数,左半段起点,右半段起点
    while (i <= mid && j <= r) {
        if (a[i] <= a[j])    tmp[k ++] = a[i ++]; //有序
        else {
            tmp[k ++] = a[j ++];
            res += mid - i + 1; //夹着的那段就是逆序对的数量
        }
    }

    while (i <= mid)    tmp[k ++] = a[i ++];
    while (j <= r)  tmp[k ++] = a[j ++];
    for (int i = l, j = 0; i <= r; i ++, j ++)    a[i] = tmp[j];
    return res;
}

高精度

高精加

vector<int> add(vector<int>& A, vector<int>& B) {
    if (A.size() < B.size())    return add(B, A);//大的放前面
    vector<int>C;
    int t = 0;
    for (int i = 0; i < A.size(); i++) {
        t += A[i];
        if (i < B.size())    t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    if (t)    C.push_back(t);
    return C;
}

高精减

bool cmp(vector<int> &A, vector<int> &B) { //先 比较,然后大减小
    if (A.size() != B.size()) return A.size() > B.size();
    for (int i = A.size() - 1; i >= 0; i -- ) {
        if (A[i] != B[i])    return A[i] > B[i];
    }
    return true;
}

vector<int> sub(vector<int> &A, vector<int> &B) {
    vector<int> C;
    for (int i = 0, t = 0; i < A.size(); i ++ ) {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

高精乘

vector<int> mult(vector<int>& A, int b) {
    t = 0;
    for (int i = 0; i < A.size()||t; i++) {
        if (i < A.size())
            t += A[i]*b;
        C.push_back(t % 10);
        t /= 10;
    }
    if (C.size() > 1 && C.back() == 0)
        C.pop_back();

    return C;
}

高精除

vector<int> div(vector<int>& A, int b, int &r) {
    r = 0;
    for (int i = A.size()-1; i >= 0 ; i--) {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0)
        C.pop_back();
    return C;
}

滑动窗口

    for (int i = 1; i <= n; i ++){ //szie=k
        while (!q.empty() && i - k >= q.front())    q.pop_front();
        while (!q.empty() && a[q.back()] >= a[i])    q.pop_back();
        q.push_back(i);
        if (i >= k)    cout << a[q.front()] <<' ';
    }

dp

背包dp

01背包

    for(int i = 1; i <= n; i ++)
        for(int j = V; j >= v[i]; j --){
            dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
        }
/*
//求具体方案
    for (int i = n; i >= 1; i --)
        for (int j = 0; j <= m; j ++) {
            f[i][j] = f[i + 1][j];
            if (j >= v[i])
                f[i][j] = max (f[i + 1][j], f[i + 1][j - v[i]] + w[i]);
        }

    int j = m, cnt = 0;
    for (int i = 1; i <= n; i ++) {
        if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i])
            cout << i << ' ', j -= v[i];
    }
*/

完全背包

    for(int i = 1; i <= n; i ++)
        for(int j = v[i]; j <= V; j ++){
            dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
        }

多重背包

    for(int i = 1; i <= n; i ++){
        cin >> a >> b >> s;
        int k = 1;
        while(k <= s){
            cnt ++;//别忘了cnt
            v[cnt] = a * k;
            w[cnt] = b * k;
            s -= k;//一定要先剪掉啊
            k *= 2;//顺序反了就寄掉
        }
        if(s){
            cnt ++;//打包数
            v[cnt] = a * s;
            w[cnt] = b * s;//小心变量cnt
        }//先加后用
    }
    n = cnt;//用打好包的来代替n,后面就可以实现01背包了

分组背包

    for(int i =1; i <=n; i ++)
        for(int j = V; j >= 0; j --)//不只是这一组,不能直接把边界设置在这里
            for(int  k = 0; k < s[i]; k ++){
                if(j >= v[i][k])//判断条件转移到这里
                    dp[j] = max(dp[j], dp[j - v[i][k]] + w[i][k]);
            }//以k为组

区间dp

    for(int len = 2; len <= n; len ++)
        for(int i = 1; i <= n - len + 1; i ++){
            int l = i, r = i + len - 1;
            dp[l][r] = INF;
            for(int k = l ; k < r; k ++)
                dp[l][r] = min(dp[l][r], dp[l][k] + dp[k+1][r] + s[r] - s[l-1]);
        }

树形dp

void dfs (int u) {
    f[u][0] = 0;
    f[u][1] = w[u];
    for (auto v : e[u]) {
        dfs (v);
        f[u][0] += max (f[v][1], f[v][0]);
        f[u][1] += f[v][0];
    }
}

期望dp

dag上期望dp

double cal () {
    for (int i = 1; i <= n; i++)    f[i] = 0.0;
    for (int i = n; i >= 1; i--) {
        //f[i] = 1;
        int fm = v[i].size (); //等概率
        double maxn = 0;
        for (auto j : v[i]) {
            double tt = f[j.first] + j.second; //vector存图{终点,距离}
            maxn = max (maxn, tt);
            f[i] += tt / fm;
        }
    }
    return f[1];
}

数位dp

ll f[N][10], n; //f[i][j]:长度为i,余数为j

ll dfs(int pos,int limit, int mod){
	if(!pos) {
        if (mod)    return 0;
        else    return 1;
    }
	if(!limit && f[pos][mod] != -1)    return f[pos][mod];
	ll res = 0, up = limit ? a[pos] : 9;
	for(int i = 0; i <= up;i++) {
		if(i == 4)  continue;	
		res += dfs(pos - 1,limit && i == up, (mod * 10 + i) % 7);		
	}
	return limit ? res : f[pos][mod] = res;
}

ll dp(ll n) {
	memset(f, -1, sizeof f);
	int len = 0;
	while(n)    a[++len] = n % 10, n /= 10;
	return dfs(len ,1, 0) - 1;
}

数学

快速幂

ll qmi(ll a, ll k, ll p){
    ll res = 1;
    while(k){
        if(k & 1)
            res = (ll)res * a % p;
        a = (ll)a * a % p;
        k >>= 1;
    }
    return res;
}

取模的时候一定要加到正数为止

数学小结论

线性筛

void get_prime(int n){
    for(int i = 2; i <= n; i ++){
        if(!vis[i])    primes[cnt++] = i;
        for(int j = 0; primes[j] <= n / i; j ++){
            vis[primes[j] * i] = true;
            if(i % primes[j] == 0)    break;
        }
    }
}

求逆元

p为质数

费马小定理:qmi(x,p-2,p)
线性递推:

inv[1] = 1;
for (int i = 2; i <= n; i++) {
    inv[i] = (p - p / i) * inv[p % i] % p;
}

p不为质数

扩欧

ll x = 0, y = 0;
int d = exgcd(a, p, x, y);
if (d == 1) 	cout << (1ll * x + p) % p << endl;
else 	puts("impossible");

扩展欧几里得

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

int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}

线性同余方程

解线性同余方程

// 求 a * x = b (mod m) 的解
ll modequ(ll a, ll b, ll m) {
    ll x, y;
    ll d = exgcd(a, m, x, y);
    if (b % d != 0) return -1;
    m /= d; a /= d; b /= d;
    x = x * b % m;
    if (x < 0) x += m;
    return x;
}

合并线性同余方程 EXCRT

// 合并两个同余方程
void merge(ll &a, ll &b, ll c, ll d) { // d <= 10^9
    // bt = c - a(mod d)
    if (a == -1 && b == -1) return;
    ll x, y;
    ll g = exgcd(b, d, x, y);
    //bx = g(mod d)
    if ((c - a) % g != 0) {
        a = b = -1;
        return;
    }
    d /= g; // d'
    ll t0 = ((c - a) / g) % d * x % d;
    if (t0 < 0) t0 += d;
    // t = t0 (mod d')
    a = b * t0 + a;
    b = b * d;
}

欧拉函数

ll phi (ll n) {
    ll ans = n;
    for (int i = 2; i*i <= n; i++) {
        if (n % i == 0) {
            ans = ans / i * (i - 1); //*(1-1/i)
            while (n % i == 0)  n /= i;
        }
    }
    if (n > 1)  ans = ans / n * (n - 1); //还剩下就再乘
    return ans;
}

筛法求欧拉

ll get_prime(int x) {
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i])    primes[cnt++] = i, phi[i] = i - 1;
        for (int j = 0; primes[j]<= x / i; j++) {
            vis[i * primes[j]] = true;
            if (i % primes[j] == 0) {
                phi[primes[j] * i] = phi[i] * primes[j];
                break;
            }
            phi[primes[j] * i] = phi[i] * (primes[j] - 1);
        }
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++)    ans += phi[i];
    return ans;
}

求组合数

二项式定理:

ll fact[N], infact[N];

ll qmi(ll a, ll k, ll p){
    ll res = 1;
    while(k){
        if(k & 1)
            res = (ll)res * a % p;
        a = (ll)a * a % p;
        k >>= 1;
    }
    return res;
}

ll C (int a, int b) {
    if (a < b)  return 0;
    return fact[a] * infact[b] % mod * infact[a - b] % mod;
}

void init () {
    fact[0] = infact[0] = 1;
    for(int i = 1; i < N; i++){
        fact[i] = (ll)fact[i-1] * i % mod;
        infact[i] = (ll)infact[i-1] * qmi(i, mod - 2,mod) % mod;
    }
}

线性筛法求Mobius函数

void init (int x) { //线性筛法求mobius函数
    mobius[1] = 1;
    int cnt = 0;
    for (int i = 2; i <= x; i++) {
        if (!vis[i])    prime[++cnt] = i, mobius[i] = -1;
        for (int j = 1; i * prime[j] <= x; j++) {
            int t = i * prime[j];
            vis[t] = true;
            if (i % prime[j] == 0) {
                mobius[t] = 0;
                break;
            }
            mobius[t] = mobius[i] * -1;
        }
    }
    for (int i = 1; i <= x; i++)    sum[i] = sum[i-1] + mobius[i];
}

线性基

struct linear_basis {
    ll num[63];
    bool insert (ll x) {
        for (int i = 62; i >= 0; i--) {
            if ((x >> i & 1) == 0)  continue;
            if (num[i] == 0) {
                num[i] = x;
                return true;
            }
            else    x ^= num[i];
        }
        return false;
    }

    ll querymin (ll x) {
        for (int i = 62; i >= 0; i --) {
            x = min (x, x ^ num[i]);
        }
        return x;
    }

    ll querymax (ll x) {
        for (int i = 62; i >= 0; i --) {
            x = max (x, x ^ num[i]);
        }
        return x;
    }
}T;

字符串

KMP

void Next(string &p, vi &ne){
    for (int i = 1, j = 0; i < n; i ++){
        while (j && p[i] != p[j])
            j = ne[j - 1];
        if (p[i] == p[j])
            j ++;
        ne[i] = j;
    }
}

void KMP (string &s, string &p, int begin){
    vi ne(n);
    Next (p, ne);
    for (int i = begin, j = 0; i < m; i ++){
        while (j && s[i] != p[j])
            j = ne[j - 1];
        if (s[i] == p[j])
            j ++;
        if (j == n){
            cout << i - n + 1 << ' ';
            j = ne[j - 1];
        }

    }
}

Z函数(扩展KMP)

vector<int> ExKMP (string s) { //从0开始
    int n = s.size ();
    vector<int> z(n);
    for (int i = 1, l = 0, r = 0; i < n; i++) {
        if (i <= r && z[i-l] < r - i + 1)   z[i] = z[i-l];
        else {
            z[i] = max (0, r - i + 1);
            while (i + z[i] < n && s[z[i]] == s[i + z[i]])  z[i] ++; //暴力匹配
        }
        if (i + z[i] > r)   l = i, r = i + z[i] - 1; //更新l,r
    }
    return z;
}

Trie

void insert(char *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(char *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];
}

最大异或对(01Trie)

void insert(int x){
    int p = 0;
    for (int i = 30; i >= 0; i --){//从高到低
        int t = x >> i & 1;//取出i位
        if (!son[p][t])
            son[p][t] = ++ idx;
        p = son[p][t];
    }
}

int query(int x){
    int p = 0, res = 0;
    for (int i = 30; i >= 0; i --){
        int t = x >> i & 1;
        if (son[p][!t]){//不同数
            res += 1 << i;//局部值
            p = son[p][!t];
        }
        else
            p = son[p][t];//找下一个
    }
    return res;
}

最小表示法

int find (char* s){ //原s复制一倍之后
    int i = 0, j = 1;
    while (i < n && j < n){
        int k = 0;
        while (k < n && s[i + k] == s[j + k])    k ++;
        if (k == n)    break;
        if (s[i + k] > s[j + k])    i += k + 1;
        else    j += k + 1;
        if (i == j)    j ++;//不能走到相同的地方
    }
    int k = min (i, j);
    s[k + n] = 0;
    return k; //最小表示法的起点
} 

字符串哈希

双哈希

const int N = 2e6 + 5; //两倍n
const int mod1 = 1e9 + 7, mod2 = 1e9 + 9;

struct pii {
    int p1, p2;
    pii () {}
    pii (int p1_, int p2_) : p1(p1_), p2(p2_) {}
    pii operator + (pii t) {
        t.p1 = (this->p1 + t.p1) % mod1;
        t.p2 = (this->p2 + t.p2) % mod2;
        return t;
    }

    pii operator - (pii t) {
        t.p1 = (this->p1 - t.p1 + mod1) % mod1;
        t.p2 = (this->p2 - t.p2 + mod2) % mod2;
        return t;
    }
    
    pii operator * (pii t) {
        t.p1 = (this->p1 * t.p1) % mod1;
        t.p2 = (this->p2 * t.p2) % mod2;
        return t;
    }

    bool operator == (pii t) {
        if (t.p1 == this->p1 && t.p2 == this->p2)   return true;
        return false;
    }
}p[N], pre[N], suf[N]; //pow值, 前缀哈希和, 后缀哈希和(左闭右开)

signed main() {
    int n;
    string t;
    cin >> n >> t;
    pii P = {131, 13331}; //进制
    p[0] = {1, 1};
    for (int i = 1; i <= 2 * n; i++) {
        p[i] = p[i-1] * P;
        pii tt = {t[i-1] - 'a', t[i-1] - 'a'};
        pre[i] = pre[i-1] * P + tt;
    }
    for (int i = n * 2; i; i--) {
        pii tt = {t[i-1] - 'a', t[i-1] - 'a'};
        suf[i] = suf[i+1] * P + tt;
    }
    //后略
}

图论

树的重心

将这个点删除后,剩余各个连通块中点数的最大值最小

int dfs(int x){
    int sum = 1, ans = 0;
    vis[x] = true;
    for (int i = h[x]; i != -1; i = ne[i]){
        int j = e[i];
        if (!vis[j]){
            int s = dfs(j);
            ans = max(ans, s);
            sum += s;
        }
    }
    ans = max (ans, n - sum);//不含自身
    res = min (res, ans);//每一个点当中取最小的那个
    return sum;
}//返回以x为根的子数中的点数

拓扑排序

bool topsort(){
    queue<int> q;
    for(int i = 1;i <= n; i++)    if(d[i] == 0) q.push(i);
    while(q.size()){
        int t = q.front();
        top[cnt++] = t;
        q.pop();
        for(int i = h[t];i != -1; i = ne[i]){
            int j = e[i];
            d[j] --;
            if(d[j] == 0) q.push(j); 
        }
    }
    return cnt >= n;
}

最短路

dijkstra

int dijkstra () {
    memset (dis, 0x3f, sizeof dis);
    dis[1] = 0;
    //vis[1] = true;

    for (int i = 1; i <= n; i ++) {
        int t = -1;
        for (int j = 1; j <= n; j ++) {
            if (!vis[j] && (t == -1 || dis[j] < dis[t]))    t = j;
        }   
        for (int j = 1; j <= n; j ++)    dis[j] = min (dis[j], a[t][j] + dis[t]);
        vis[t] = true;
    }
    if (dis[n] == 0x3f3f3f3f)    return -1;
    return dis[n];
}

堆优化

int dijkstra(){
    memset (dis, 0x3f, sizeof dis);
    dis[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>>heap;
    heap.push({0, 1});//dis num

    while (!heap.empty()){
        auto t = heap.top();
        heap.pop();
        int num = t.second, distance = t.first;
        if (vis[num])    continue;//pass
        vis[num] = true;

        for (int i = h[num]; i != -1; i = ne[i]){
            int j = e[i];
            if (dis[j] > distance + w[i]){
                dis[j] = distance + w[i];
                heap.push({dis[j], j});
            }
        }
    }
    if (dis[n] == 0x3f3f3f3f)    return -1;
    return dis[n];
}

Bellman_ford

void bellman_ford(){
    memset (dis, 0x3f, sizeof dis);
    dis[1] = 0;
    for (int i = 0; i < k; i ++){ //最多经过k条边的最短距离
        memcpy (backup, dis, sizeof dis);
        for (int j = 1; j <= n; j ++)
            for (int t = 1; t <= n; t ++){
                dis[t] = min (dis[t], backup[j] + a[j][t]);
            }
    } //dis[n] > 0x3f3f3f3f/2 表示到不了
}

SPFA

int spfa () {
    memset (dis, 0x3f, sizeof dis);
    dis[1] = 0, vis[1] = true;
    q.push (1);

    while (!q.empty()) {
        int t = q.front();
        q.pop();
        vis[t] = false;

        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dis[j] > dis[t] + w[i]){
                dis[j] = dis[t] + w[i];
                if (!vis[j])    vis[j] = true, q.push (j);
            }
        }
    }
    return dis[n];
}

SPFA判负环
统计每个点的入队次数,如果某个点入队 n 次,则有负环

bool spfa () {
	for (int i = 1; i <= n; i ++) {
		q.push(i);
		vis[i] = true;
	}
	
	while (!q.empty()) {
		int t = q.front();
		q.pop();
		vis[t] = false; 
		
		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			if (dis[j] > dis[t] + w[i]) {
				dis[j] = dis[t] + w[i];
				cnt[j] = cnt[t] + 1;
				
				if (cnt[j] >= n)	return true;
				if (!vis[j])	q.push (j), vis[j] = true;
			}
		}
	}
	return false;
}

Floyd

void floyd () {
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j ++){
            if (i == j)    a[i][j] = 0;
            else    a[i][j] = INF;
        }
    }
    for (int k = 1; k <= n; k ++) {
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= n; j ++) {
                a[i][j] = min (a[i][j], a[i][k] + a[k][j]);
            }
        }
    }
}

最小生成树

prim

int prim(){
    memset (dis, 0x3f, sizeof dis);
    dis[1] = 0;

    int ans = 0;
    for (int i = 0; i < n; i ++){
        int t = -1;
        for (int j = 1; j <= n; j ++){
            if (!vis[j] && (t == -1 || dis[t] > dis[j]))    t = j;
        }
        if (dis[t] == INF)    return INF;
        ans += dis[t];
        vis[t] = true;
        for (int j = 1; j <= n; j ++)    dis[j] = min (dis[j], a[t][j]);
    }
    return ans;
}

Kruscal

int kruskal(){
    sort (e, e + m);
    for (int i = 1; i <= n; i ++)    fa[i] = i;
    int ans = 0, cnt = 0;

    for (int i = 0; i < m; i ++){
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find(a), b = find(b);
        if (a != b){
            fa[a] = b;
            ans += w;
            cnt ++;
        }//不连通就加入集合
    }

    if (cnt < n - 1)    return INF;
    return ans;
}

二分图

染色法判二分图

bool dfs(int u, int c){
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if (color[j] == -1){
            if (!dfs(j, !c))    return false;
        }
        else if (color[j] == c)    return false;
    }
    return true;
}

bool check(){
    memset (color, -1, sizeof color);
    bool flag = true;
    for (int i = 1; i <= n; i ++){
        if (color[i] == -1 && !dfs(i, 0)) {
            flag = false;
            break;
        }
    }
    return flag;
}

二分图最大匹配

bool find (int x){
    for (int i = h[x]; i != -1;  i = ne[i]){
        int j = e[i];
        if (!vis[j]){
            vis[j] = true;
            if (match[j] == 0 || find (match[j])){
                match[j] = x;
                return true;
            }
        }
    }
    return false;
}

/* //main函数中
    for (int i = 1; i <= n1; i ++){
        memset(vis, false, sizeof vis);
        if (find(i))
            ans ++;
    }
*/

最近公共祖先LCA

倍增法

void bfs (int root) {
    memset (depth, 0x3f, sizeof depth); //记得
    queue <int> q;
    q.push (root);
    depth[root] = 1, depth[0] = 0;

    while (!q.empty()) {
        int t = q.front();
        q.pop ();

        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (depth[j] > depth[t] + 1) {
                depth[j] = depth[t] + 1;
                q.push (j);
                f[j][0] = t;
                for  (int k = 1; k <= 15; k++) {
                    f[j][k] = f[f[j][k-1]][k-1];
                }
            }
        }
    }
}

int lca (int a, int b) {
    if (depth[a] < depth[b])    swap (a, b); //确保a往上跳
    for (int k = 15; k >= 0; k--) {
        if (depth[f[a][k]] >= depth[b]) { //注意是>=
            a = f[a][k];
        }
    }
    if (a == b)     return a;
    for (int k = 15; k >= 0; k--) {
        if (f[a][k] != f[b][k]) {
            a = f[a][k], b = f[b][k];
        }
    }
    return f[a][0];
}

\(Tarjan\) 离线做法

void dfs (int v, int u) {  //1, -1
    for (int i = h[v]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == u) continue;
        d[j] = d[v] + w[i];
        dfs (j, v);
    }
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

void tarjan (int u) {  //1
    vis[u] = 1; //正在搜
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (vis[j]) continue;
        tarjan(j);
        fa[j] = u; //合并
    }

    for (auto i : q[u]) {
        int v = i.first, id = i.second;
        if (vis[v] == 2) {
            int p = find(v);
            ans[id] = d[u] + d[v] - 2*d[p];
        }
    }

    vis[u] = 2; //搜过了
}

缩点

void tarjan (int u) {
    dfn[u] = low[u] = ++ timestamp;
    stk.push (u), in_stk[u] = true;

    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (!dfn[j]) {
            tarjan(j);
            low[u] = min (low[u], low[j]);
        }
        else if (in_stk[j])     low[u] = min (low[u], dfn[j]);
    }

    if (dfn[u] == low[u]) {
        int y = 0;
        scc_cnt ++;
        do {
            y = stk.top();
            stk.pop();
            in_stk[y] = false;
            id[y] = scc_cnt, sz[scc_cnt] ++;
        } while (y != u);
    }
}

int main () {
    memset  (h, -1, sizeof h);
    cin >> n >> m;
    for (int i = 0; i < m; i ++)  {
        int a, b;
        cin >> a >> b;
        add (a, b);
    }

    for (int i = 1; i <= n; i++) {
        if (!dfn[i])    tarjan(i);
    }

    for (int t = 1; t <= n; t++) {
        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            int a = id[t], b = id[j];
            if (a ^ b)  dout[a] ++; //非拓扑图终点
        }
    }

    int cnt = 0, ans = 0; //dout=0的点数
    for (int i = 1; i <= scc_cnt; i++) {
        if (dout[i])    continue;
        cnt ++, ans += sz[i];
        if (cnt > 1) { //出度为0的点数大于1,则这些点之间无法互相到达
            ans = 0;
            break;
        }
    }
    cout << ans;
}

欧拉回路

void dfs (int u) {
    for (int &i = h[u]; ~i; ) {
        if (used[i]) {
            i = ne[i];
            continue;
        }

        used[i] = true;
        if (ver == 1)    used[i^1] = true; //无向图存边的规律

        int t;
        if (ver == 1) {
            t = i / 2 + 1; //找节点
            if (i & 1)    t = -t;
        }
        else       t = i + 1;

        //记录答案
        int j = e[i];
        i = ne[i];
        dfs (j);
        ans[cnt ++] = t;
    }
}

int main() {
    memset(h, -1, sizeof h);
    cin >> ver >> n >> m;

    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        add(a, b);
        if (ver == 1)    add(b, a); //无向边
        din[b]++, dout[a]++;
    }

    if (ver == 1) {
        for (int i = 1; i <= n; i++) {
            if (din[i] + dout[i] & 1) {//无向图:每个点的度都为偶数
                puts("NO");
                return 0;
            }
        }
    }

    else {
        for (int i = 1; i <= n; i++) {
            if (din[i] != dout[i]) { //有向图:每个点的入度等于出度
                puts("NO");
                return 0;
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        if (~h[i]) {
            dfs(i);
            break;
        }
    }
    if (cnt < m) { //不连通
        puts("NO");
        return 0;
    }
    puts("YES");
    for (int i = cnt - 1; i >= 0; --i)    cout << ans[i] << " ";
}

点分治

//长度不超过k的路有多少条
int getSize (int u, int fa) { //以u为根的子树大小
    if (vis[u])     return 0; 
    int sz = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa)    continue;
        sz += getSize (j, u);
    }
    return sz;
}

int getCenter (int u, int fa, int allSize, int &ct) { //求重心
    if (vis[u])     return 0;
    int sum = 1, maxSize = 1; //u的子树的结点个数
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa)    continue;
        int curSize = getCenter (j, u, allSize, ct);
        sum += curSize;
        maxSize = max (maxSize, curSize);
    }
    maxSize = max (maxSize, allSize - sum);
    if (maxSize <= allSize / 2)   ct = u; //不一定是重心,满足一半即可,保证logn
    return sum;
}

void getDis (int u, int fa, int dis, int &len) { //当前子树上所有点到根的距离
    if (vis[u])     return ;
    dis_cur[len++] = dis;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa)    continue;
        getDis (j, u, dis + w[i], len); //dis + w[i]记得更新距离啊!!
    }
}

int count (int *a, int sz) { //a[]中选两点满足dis<=m
    sort (a, a + sz);
    int cnt = 0; //满足条件的点对个数
    for (int i = sz - 1, j = -1; i >= 0; i--) {
        while (j < i - 1 && a[i] + a[j + 1] <= m)   j++;
        j = min (j, i - 1); //j左i右
        cnt += j + 1;
    }
    return cnt;
}

int cal (int u) { //递归计算以u为根的答案
    if (vis[u])     return 0;
    getCenter (u, -1, getSize (u, -1), u);
    vis[u] = true; //删重心

    int len = 0, cnt = 0;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        int len_cur = 0; //当前子树大小
        getDis (j, -1, w[i], len_cur);
        cnt -= count (dis_cur, len_cur); //情况3: 容斥掉同一树内的点对

        for (int k = 0; k < len_cur; k++) {
            if (dis_cur[k] <= m)    cnt++; //情况2: 某点是重心
            dis_all[len++] = dis_cur[k];
        }
    }
    cnt += count (dis_all, len); //情况3: 不同子树间的点对
    for (int i = h[u]; ~i; i = ne[i])    cnt += cal (e[i]);
    return cnt;
}

斯坦纳树

void dijkstra (int s) {
    memset (vis, false, sizeof vis);
    while (!q.empty ()) {
        auto t = q.top ();
        int ver = t.second;
        q.pop ();
        if (vis[ver])   continue;
        vis[ver] = true;

        for (int i = h[ver]; ~i; i = ne[i]) {
            int j = e[i];
            if (f[j][s] > f[ver][s] + w[i]) {
                f[j][s] = f[ver][s] + w[i];
                q.push ({f[ver][s], j});
            }
        }
    }
}

int main () {
    cin >> n >> m >> k;
    memset (h, -1, sizeof h);
    memset (f, 0x3f, sizeof f);
    for (int i = 1; i <= m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add (a, b, c), add (b, a, c);
    }
    for (int i = 1; i <= k; i++)    cin >> S[i], f[S[i]][1<<(i-1)] = 0;

    for (int s = 1; s < (1 << k); s++) {
        for (int i = 1; i <= n; i++) {
            for (int t = s & (s - 1); t; t = s & (t - 1)) {
                f[i][s] = min (f[i][s], f[i][t] + f[i][s ^ t]);
            }
            if (f[i][s] != inf)     q.push ({f[i][s], i});
        }
        dijkstra (s);
    }
    cout << f[S[1]][(1<<k) - 1];
}

网络流

EK算法

bool bfs () {
    queue<int> q;
    memset (vis, false, sizeof vis);
    memset (d, 0, sizeof d);
    memset (pre, 0, sizeof pre);
    q.push (S), vis[S] = true, d[S] = inf;

    while (!q.empty()) {
        int t = q.front();
        q.pop();

        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (vis[j] || w[i] == 0)    continue;
            pre[j] = i;
            vis[j] = true;
            d[j] = min (d[t], w[i]);

            if (j == T) return true;
            q.push (j);
        }
    }
    return false;
}

int EK () {
    int ans = 0;
    while (bfs ()) {
        ans += d[T];
        for (int i = T; i != S; i = e[pre[i]^1]) {
            w[pre[i]] -= d[T], w[pre[i]^1] += d[T];
        }
    }
    return ans;
}

Dinic

bool bfs () {
    queue <int> q;
    memset (d, -1, sizeof d);
    q.push (S), d[S] = 0, cur[S] = h[S];
    while (!q.empty ()) {
        int t = q.front();
        q.pop();

        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (d[j] != -1 || w[i] == 0)    continue;
            d[j] = d[t] + 1;
            cur[j] = h[j];
            if (j == T) return true;
            q.push (j);
        }
    }
    return false;
}

int find (int u, int limit) {
    if (u == T)     return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
        cur[u] = i; //当前弧优化
        int j = e[i];
        if (d[j] != d[u] + 1 || w[i] == 0)  continue;
        int t = find (j, min (w[i], limit - flow));
        if (t == 0)     d[j] = -1;
        w[i] -= t, w[i^1] += t, flow += t;
    } 
    return flow;
}

int dinic () {
    int r = 0, flow;
    while (bfs ())  while (flow = find (S, inf))    r += flow;
    return r;
}

数据结构

ST表

void pre() { 
    Logn[1] = 0;
    Logn[2] = 1;
    for (int i = 3; i < maxn; i++)    Logn[i] = Logn[i / 2] + 1;
}

int main() {
    int n = read(), m = read();
    for (int i = 1; i <= n; i++)    f[i][0] = read();
    pre();
    for (int j = 1; j <= logn; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); // ST表具体实现
    for (int i = 1; i <= m; i++) {
        int x = read(), y = read();
        int s = Logn[y - x + 1];
        printf("%d\n", max(f[x][s], f[y - (1 << s) + 1][s]));
    }
    return 0;
}

并查集

void init () {
    for (int i = 1; i <= n; i ++)    fa[i] = i, sz[i] = 1;
}

int find (int x) {
    if (x != fa[x])    fa[x] = find (fa[x]);
    return fa[x];
}

void Union (int x, int y) { //启发式合并
    x = find (x), y = find (y);
    if (x == y) return;
    if (sz[x] > sz[y])    swap(xx, yy);
    fa[x] = y;
    sz[y] += sz[x]; //注意不要反了
}

带权并查集

int find (int x) {
    if (x != fa[x]) {
 	int t = fa[x]; //记录原父节点编号
	fa[x] = find(fa[x]); //父节点变为根节点,此时value[x]=父节点到根节点的权值
	v[x] += v[t]; //当前节点的权值加上原本父节点的权值      
    }
    return fa[x];
}

void Union (int x, int y) {
    int xx = find (x), yy = find (y);
    if (xx != yy) {
        fa[xx] = yy;
        v[xx] = v[y] - v[x] + s; //有时候可能会取模
    }
}

树状数组

ll lowbit (ll x) {
    return (x & (-x));
}

void add (int x, int d) {
    while (x <= n)  tr[x] += d, x += lowbit (x);
}

ll query (ll x) {
    ll s = 0;
    while (x > 0)   s += tr[x], x -= lowbit (x);
    return s;
}

线段树

单调修改

struct st{
    int l, r;
    int v;
}st[N * 4];

void build (int u, int l, int r){
    st[u] = {l, r};
    if (l == r)    return;
    int mid = l + r >> 1;
    build (u << 1, l, mid);
    build (u << 1 | 1, mid + 1, r);
}

int query(int u, int l, int r){
    if (st[u].l >= l && st[u].r <= r)    return st[u].v;
    int mid = st[u].l + st[u].r >> 1;
    int v = 0;
    //注意注意!此处l r顺序不能变
    if (l <= mid)   	v = query (u << 1, l, r);
    if (r > mid)    	v = max (v, query (u << 1 | 1, l, r));
    return v;
}

void modify (int u, int x, int v){
    if (st[u].l == x && st[u].r == x){
        st[u].v = v;
        return;
    }
    int mid = st[u].l + st[u].r >> 1;
    if (x <= mid)    modify (u << 1, x, v);
    else    modify (u << 1 | 1, x, v);
    st[u].v = max (st[u << 1].v, st[u << 1 | 1].v);
}

查询区间最大连续子段和

struct tree{
    int l, r, sum;
    int lmax, rmax, tmax;//最大前后缀和, 最大子段和
}st[4 * N];

void pushup (tree &u, tree &l, tree &r){
    u.sum = l.sum + r.sum;
    u.lmax = max (l.lmax, l.sum + r.lmax);//左半段中的最大 or 左半段和+右半段中的最大
    u.rmax = max (r.rmax, r.sum + l.rmax);//右半段中的最大 or 右半段和+左半段中的最大
    u.tmax = max (max (l.tmax, r.tmax), l.rmax + r.lmax);//左半段中的最大 or 右半段中的最大 or 左半段后缀和+右半段前缀和
}

void pushup (int u){
    pushup (st[u], st[u << 1], st[u << 1 | 1]);
}

void build (int u, int l, int r){
    if (l == r){
        st[u] = {l, r, a[r], a[r], a[r], a[r]};
        return;
    }
    st[u] ={l, r};
    int mid = l + r >> 1;
    build (u << 1, l, mid), build (u << 1 | 1, mid + 1, r);
    pushup (u);
}

void modify (int u, int x, int v){
    if (st[u].l == x && st[u].r == x)    st[u] = {x, x, v, v, v, v};
    else{
        int mid = st[u].l + st[u].r >> 1;
        if (x <= mid)    modify (u << 1, x, v);
        else    modify (u << 1 | 1, x, v);
        pushup (u);
    }
}

tree query (int u, int l, int r){
    if (st[u].l >= l && st[u].r <= r)    return st[u];
    int mid = st[u].l + st[u].r >> 1;
    if (r <= mid)    return query (u << 1, l, r);
    else if (l > mid)    return query (u << 1 | 1, l, r);
    else{
        auto le = query (u << 1, l, r);
        auto ri = query (u << 1 | 1, l, r);
        tree ans;
        pushup (ans, le, ri);
        return ans;
    }
}

区间最大公约数

struct Node {
    int l, r;
    ll sum, d;
}st[N * 4];

ll gcd (ll a, ll b){
    return b ? gcd (b, a % b) : a;
}

void pushup (Node &u, Node &l, Node &r){
    u.sum = l.sum + r.sum;
    u.d = gcd (l.d, r.d);
}//单点修改

void pushup (int u){
    pushup (st[u], st[u << 1], st[u << 1 | 1]);
}//修改全部

void build (int u, int l, int r){

    if (l == r){
        ll t = w[r] - w[r - 1];
        st[u] = {l, r, t, t};
        return;
    } //只修改叶子节点的值

    st[u] = {l, r};  
    int mid = l + r >> 1;
    build (u << 1, l, mid);
    build (u << 1 | 1, mid + 1, r);
    pushup (u);
}

void modify (int u, int x, ll v){ //这个地方v忘记写成ll了
    if (st[u].l == x && st[u].r == x){
        ll t = st[u].sum + v;
        st[u] = {x, x, t, t};
    }
    else {
        int mid = st[u].l + st[u].r >> 1;
        if (x <= mid)
            modify (u << 1, x, v);
        else
            modify (u << 1 | 1, x, v);
        pushup (u);
    }
}

Node query (int u, int l, int r){
    if (st[u].l >= l && st[u].r <= r){
        return st[u];
    }
    int mid = st[u].l + st[u].r >> 1;
    if (r <= mid)
        return query (u << 1, l, r);
    else if (l > mid)
        return query (u << 1 | 1, l, r);
    else{
        auto ll = query (u << 1, l, r), rr = query (u << 1 | 1, l, r);
        Node res;
        pushup (res, ll, rr);
        return res;
    }
}

int main (){
    cin >> n >> m;

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

    while (m --){
        ll l, r, d;
        char op;
        cin >> op;
        if (op == 'C'){
            cin >> l >> r >> d;
            modify (1, l, d);
            if (r + 1 <= n) //防越界
                modify (1, r + 1, -d);
        }

        else {
            cin >> l >> r;
            auto le = query (1, 1, l);
            Node ri ({0, 0, 0, 0});
            if (l + 1 <= r) //防越界
                ri = query (1, l + 1, r);
            cout << abs (gcd (le.sum, ri.d)) << endl;//正余数
        }
    }
}

区间修改

struct Node {
    int l, r;
    ll sum, lazy;
}st[N * 4]; //千万别忘了 * 4

void pushup (int u){
    st[u].sum = st[u << 1].sum + st[u << 1 | 1].sum;
}

void pushdown (int u){
    auto &root = st[u], &st_l = st[u << 1], &st_r = st[u << 1 | 1];
    if (root.lazy){
        st_l.lazy += root.lazy, st_l.sum += (ll)(st_l.r - st_l.l + 1) * root.lazy;
        st_r.lazy += root.lazy, st_r.sum += (ll)(st_r.r - st_r.l + 1) * root.lazy;
        root.lazy = 0;                        //就是这里,更新的时候要写各自的区间
    }
}

void build (int u, int l, int r){
    if (l == r){
        st[u] = {l, r, a[r], 0};
        return;
    }

    st[u] = {l, r};
    int mid = l + r >> 1;
    build (u << 1, l, mid);
    build (u << 1 | 1, mid + 1, r);
    pushup (u);
}

void modify (int u, int l, int r, int d) {
    if (st[u].l >= l && st[u].r <= r){
        st[u].sum += (ll)(st[u].r - st[u].l + 1) * d;
        st[u].lazy += d;
        return;
    }

    pushdown (u); //懒标记传给子
    int mid = st[u].l + st[u].r >> 1;

    if (l <= mid)
        modify (u << 1, l, r, d);
    if (r > mid)
        modify (u << 1 | 1, l, r, d);

    pushup (u);
}

ll query (int u, int l, int r){
    if (st[u].l >= l && st[u].r <= r){
        return st[u].sum;
    }
    pushdown (u);
    int mid = st[u].l + st[u].r >> 1;
    ll res = 0;
    if (l <= mid)
        res += query (u << 1, l, r);
    if (r > mid)
        res += query (u << 1 | 1, l, r);
    return res;

}

扫描线求面积并

struct Seg {
    double x, y, yy;
    int k;

    bool operator < (const Seg  &t) const {
        return x < t.x;
    }

}seg[N * 2];

struct Node {
    int l, r;
    int cnt;
    double sum;  //区间被覆盖的次数以及区间长度
}st[N * 8];

int find (double y) {
    return lower_bound (v.begin(), v.end(), y) - v.begin();
}  //找离散化后y的下标 

void pushup (int u) {
    if (st[u].cnt)
        st[u].sum = v[st[u].r + 1] - v[st[u].l];  //区间更新
    else {
        if (st[u].l == st[u].r)
            st[u].sum = 0;  //一个点
        else
            st[u].sum = st[u << 1].sum + st[u << 1 | 1].sum; //熟悉的区间更新
    }
}

void build (int u, int l, int r) {
    if (l == r) {
        st[u] = {l, r, 0, 0};
        return ;
    }

    st[u] = {l, r, 0, 0};
    int mid = l + r >> 1;
    build (u << 1, l, mid);
    build (u << 1 | 1, mid + 1, r);

}

void modify (int u, int l, int r, int k) {
    if (st[u].l >= l && st[u].r <= r) {
        st[u].cnt += k;
        pushup (u);
        return ;
    }

    int mid = st[u].l + st[u].r >> 1;
    if (l <= mid)
        modify (u << 1, l, r, k);
    if (r > mid)
        modify (u << 1 | 1, l, r, k);
    pushup (u);
}

int main () {
    int n, T = 1;
    while (cin >> n, n) {
            v.clear();

        for (int i = 0, j = 0; i < n; i ++) {  //后面有用到n的话最好不要轻易n--
            double a, b, c, d;

            cin >> a >> c >> b >> d;  //挖草!!是输入出了问题。。。x1 y1 x2 y2的顺序
            seg[j ++] = {a, c, d, 1};  //左纵边
            seg[j ++] = {b, c, d, -1}; //右纵边
            v.push_back (c), v.push_back (d);
        }

        sort (v.begin(), v.end());
        v.erase (unique (v.begin(), v.end()), v.end());  //离散化

        build (1, 0, v.size() - 2);  // n个节点对应(n-1)个区间,下标从0开始,故为(n-2)
        sort (seg, seg + n * 2);  // 纵边按x升序排序

        double ans = 0;
        for (int i = 0; i < n * 2; i ++) {
            if (i > 0)  // 从第2条纵边开始
                ans += st[1].sum * (seg[i].x - seg[i - 1].x);  //小矩形
            modify (1, find (seg[i].y), find(seg[i].yy) - 1, seg[i].k); 
        }
        printf("Test case #%d\n", T ++ );
        printf("Total explored area: %.2lf\n\n", ans);
    }
}

可持久化线段树

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 5, M = 1e4 + 5;
int n, q, a[N], root[N], idx;
vector<int> v; //离散化

int find (int x) { //离散化后查找下标
    return lower_bound (v.begin (), v.end (), x) - v.begin ();
}

struct Node {
    int l, r, cnt;
}st[N * 4 + N * 17]; //4N + NlogN

int build (int l, int r) {
    int p = ++idx;
    if (l == r) return p;
    int mid = l + r >> 1;
    st[p].l = build (l, mid), st[p].r = build (mid + 1, r);
    return p;
}

int insert (int u, int l, int r, int x) {
    int p = ++idx;
    st[p] = st[u];
    if (l == r) {
        st[p].cnt++;
        return p;
    }
    int mid = l + r >> 1;
    if (x <= mid)    st[p].l = insert (st[u].l, l, mid, x);
    else    st[p].r = insert (st[u].r, mid + 1, r, x);
    st[p].cnt = st[st[p].l].cnt + st[st[p].r].cnt;
    return p;
}

int query (int q, int p, int l, int r, int k) {
    if (l == r) return r;
    int mid = l + r >> 1;
    int cnt = st[st[q].l].cnt - st[st[p].l].cnt;
    if (k <= cnt)   return query (st[q].l, st[p].l, l, mid, k);
    return query (st[q].r, st[p].r, mid + 1, r, k - cnt);
}

int main () {
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        v.push_back (a[i]);
    }
    sort (v.begin (), v.end ());
    v.erase (unique (v.begin (), v.end ()), v.end ());
    int m = v.size ();
    root[0] = build (0, m - 1);
    for (int i = 1; i <= n; i++)    root[i] = insert (root[i-1], 0, m - 1, find (a[i]));

    while (q--) {
        int l, r, k;
        cin >> l >> r >> k;
        cout << v[query (root[r], root[l-1], 0, m - 1, k)] << endl;
    }
    //cout << log2(N);
}

珂朵莉树

有区间赋值操作且数据随机的题目
(玄学暴力)

struct node {
    ll l, r;
    mutable ll v;
    node(ll l, ll r, ll v) : l(l), r(r), v(v) {}
    bool operator<(const node &o) const { return l < o.l; }
};
set<node> tree;
auto split(ll pos) {
    auto it = tree.lower_bound(node(pos, 0, 0));
    if (it != tree.end() && it->l == pos)
        return it;
    it--;
    ll l = it->l, r = it->r, v = it->v;
    tree.erase(it);
    tree.insert(node(l, pos - 1, v));
    return tree.insert(node(pos, r, v)).first;
}
void assign(ll l, ll r, ll v) {
    auto end = split(r + 1), begin = split(l);
    tree.erase(begin, end);
    tree.insert(node(l, r, v));
}

其它

sqrt微调

int sqrtx (int x) {
    int k = sqrt (x);
    while (k * k >= x)  k--;
    while ((k + 1) * (k + 1) < x)   k++;
    return k;
}
posted @ 2023-04-01 18:12  Sakana~  阅读(145)  评论(4编辑  收藏  举报