xcpc自用板子

可持久化线段树

/*
可持久化线段树:区间取min
每次update之后返回新版本的线段树的根结点。
查询操作传入相应版本的线段树的根结点即可。
因为要可持久化,所以必须标记永久化,不能pushdown。
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 110000;
const int inf = 1 << 30;
struct node {
	int l, r, lc, rc;
	int minv, tag;
} t[maxn * 20];
int cur = 0, mn;
void init() {
	cur = 0;
}
void maintain(int p) {
	int lc = t[p].lc, rc = t[p].rc;
	t[p].minv = t[p].tag;
	if (t[p].r > t[p].l) {
		t[p].minv = min({ t[lc].minv, t[rc].minv, t[p].tag });
	}
}
int build(int L, int R) { //只要计算mid的方式是(L + R) >> 1而不是(L + R) / 2,就可以建立负坐标线段树。
	int p = ++cur;
	t[p].l = L;
	t[p].r = R;
	t[p].tag = inf; //重置结点标记
	if (L < R) {
		int mid = (L + R) >> 1;
		t[p].lc = build(L, mid);
		t[p].rc = build(mid + 1, R);
	}
	maintain(p);
	return p;
}
int update(int i, int L, int R, int v) { //区间取min
	int p = ++cur; t[p] = t[i];
	if (L <= t[p].l && R >= t[p].r) {
		t[p].tag = min(t[p].tag, v);
	}
	else {
		int mid = (t[p].l + t[p].r) >> 1;
		if (L <= mid)
			t[p].lc = update(t[p].lc, L, R, v);
		if (R > mid)
			t[p].rc = update(t[p].rc, L, R, v);
	}
	maintain(p);
	return p;
}
void query(int p, int L, int R) { //调用之前设置mn = inf,查询区间最小值
	mn = min(mn, t[p].tag);
	if (L <= t[p].l && R >= t[p].r) {
		mn = min(mn, t[p].minv);
	}
	else {
		int mid = (t[p].l + t[p].r) >> 1;
		if (L <= mid) 
			query(t[p].lc, L, R);
		if (R > mid) 
			query(t[p].rc, L, R);
	}
}
int main() {
	init();
	int root[10], n = 10;
	root[0] = build(1, n);
	root[1] = update(root[0], 1, 7, 10);
	root[2] = update(root[1], 5, 8, 0);
	mn = inf; query(root[2], 1, 5);
	printf("%d\n", mn);
	return 0;
}

线段树

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <numeric>
#include <algorithm>
#include <random>
using namespace std;
const int maxn = 210000, offset = 210000;
const int inf = 1 << 30;
struct tmp {
	int data[maxn * 5];
	int& operator[] (int idx) {
		return data[idx + offset];
	}
}A;

#define lc t[p].lchild
#define rc t[p].rchild
int cur = 0, tot, mn, mx;
struct segment {
	int l, r, lchild, rchild;
	int sum, min, max, set, add;
}t[maxn * 4];
void init() {
	cur = 0;
}
inline void maintain(int p) {
	t[p].sum = t[lc].sum + t[rc].sum;
	t[p].min = min(t[lc].min, t[rc].min);
	t[p].max = max(t[lc].max, t[rc].max);
}
inline void mark(int p, int setv, int addv) { //给结点打标记
	if (setv >= 0) {
		t[p].set = setv; t[p].add = 0;
		t[p].min = t[p].max = setv;
		t[p].sum = setv * (t[p].r - t[p].l + 1);
	}
	if (addv) {
		t[p].add += addv;
		t[p].min += addv;
		t[p].max += addv;
		t[p].sum += addv * (t[p].r - t[p].l + 1);
	}
}
inline void pushdown(int p) { //pushdown将标记传递给子结点,不影响当前结点的信息。
	mark(lc, t[p].set, t[p].add);
	mark(rc, t[p].set, t[p].add);
	t[p].set = -1;
	t[p].add = 0;
}
int build(int L, int R) { //只要计算mid的方式是(L + R) >> 1而不是(L + R) / 2,就可以建立负坐标线段树。
	int p = ++cur;
	t[p].l = L;
	t[p].r = R;
	t[p].add = 0; t[p].set = -1; //清空结点标记
	if (t[p].l == t[p].r) {
		mark(p, 0, A[L]);
	}
	else {
		int mid = (t[p].l + t[p].r) >> 1;
		lc = build(L, mid);
		rc = build(mid + 1, R);
		maintain(p);
	}
	return p;
}
void update(int p, int L, int R, int op, int v) {
	if (L <= t[p].l && R >= t[p].r) {
		if (op == 0) 
			mark(p, -1, v);
		else 
			mark(p, v, 0);
	}
	else {
		pushdown(p); //如果没有pushdown只需要在最后调用一次maintain即可。
		int mid = (t[p].l + t[p].r) >> 1;
		if (L <= mid)
			update(lc, L, R, op, v);
		if (R > mid)
			update(rc, L, R, op, v);
		maintain(p);
	}
}
void update(int p, int pos, int v) { //单点修改
	if (t[p].l == t[p].r) {
		mark(p, -1, v);
	}
	else {
		pushdown(p);
		int mid = (t[p].l + t[p].r) >> 1;
		if (pos <= mid)
			update(lc, pos, v);
		else
			update(rc, pos, v);
		maintain(p);
	}
}
void query(int p, int L, int R) { //调用之前要设置:mn = inf; mx = -inf; tot = 0;							
	if (L <= t[p].l && R >= t[p].r) {
		tot += t[p].sum;
		mn = min(mn, t[p].min);
		mx = max(mx, t[p].max);
	}
	else {
		pushdown(p);
		int mid = (t[p].l + t[p].r) >> 1;
		if (L <= mid)
			query(lc, L, R);
		if (R > mid)
			query(rc, L, R);
	}
}
int main() {
	// default_random_engine e;
	// int n = 100000, m = 100000;
	// uniform_int_distribution<int> d(-n, n);
	// for (int i = -n; i <= n; ++i)
	// 	A[i] = d(e);
    int n=10;
    for(int i=1;i<=10;i++){
        A[i]=5;
    }
	init();
	int root = build(1, n);
    mn = inf; mx = -inf; tot = 0;
    query(root,1,10);
    cout<<mn<<" "<<mx<<endl;
    update(root,2,2,0,1);
    mn = inf; mx = -inf; tot = 0;
    query(root,2,2);
    cout<<mn<<" "<<mx<<endl;
    update(root,2,4,0,-1);
    mn = inf; mx = -inf; tot = 0;
    query(root,1,4);
    cout<<mn<<" "<<mx<<endl;
	// for (int i = 1; i <= m; ++i) {
	// 	int op = rand() % 4, a = d(e), b = d(e), v = rand();
	// 	int L = min(a, b), R = max(a, b);
	// 	if (op == 0) {
	// 		for (int i = L; i <= R; ++i)
	// 			A[i] += v;
	// 		update(root, L, R, op, v);
	// 	}
	// 	else if (op == 1) {
	// 		for (int i = L; i <= R; ++i)
	// 			A[i] = v;
	// 		update(root, L, R, op, v);
	// 	}
	// 	else if (op == 2) {
	// 		mn = inf; mx = -inf; tot = 0;
	// 		query(root, L, R);
	// 		if (mn != *min_element(A.data + offset + L, A.data + offset + R + 1))
	// 			abort();
	// 		if (mx != *max_element(A.data + offset + L, A.data + offset + R + 1))
	// 			abort();
	// 		if (tot != accumulate(A.data + offset + L, A.data + offset + R + 1, 0))
	// 			abort();
	// 	}
	// 	else {
	// 		A[L] += v;
	// 		update(root, L, v);
	// 	}
	// }
	return 0;
}

网络流

//链式前向星
struct Edge
{
    int to, w, next;
}edges[MAXN];

int head[MAXN], cnt; // cnt为当前边的编号
inline void add(int from, int to, int w)
{
    edges[++cnt].w = w;    //新增一条编号为cnt+1的边,边权为w
    edges[cnt].to = to;    //该边的终点为to
    edges[cnt].next = head[from];  //把下一条边,设置为当前起点的第一条边
    head[from] = cnt;  //该边成为当前起点新的第一条边

    //反向边
    edges[++cnt].w = 0;
    edges[cnt].to = from;
    edges[cnt].next = head[to];
    head[to] = cnt;
}


int s, t, lv[MAXN], cur[MAXN]; // lv是每个点的层数,cur用于当前弧优化标记增广起点 ,s是源点,t是汇点
inline bool bfs() // BFS分层
{
    memset(lv, -1, sizeof(lv));
    lv[s] = 0;
    memcpy(cur, head, sizeof(head)); // 当前弧优化初始化
    queue<int> q;
    q.push(s);
    while (!q.empty())
    {
        int p = q.front();
        q.pop();
        for (int eg = head[p]; eg; eg = edges[eg].next)
        {
            int to = edges[eg].to, vol = edges[eg].w;
            if (vol > 0 && lv[to] == -1)
                lv[to] = lv[p] + 1, q.push(to);
        }
    }
    return lv[t] != -1; // 如果汇点未访问过说明已经无法达到汇点,此时返回false
}
int dfs(int p = s, int flow = INF)
{
    if (p == t)
        return flow;
    int rmn = flow; // 剩余的流量
    for (int eg = cur[p]; eg && rmn; eg = edges[eg].next) // 如果已经没有剩余流量则退出
    {
        cur[p] = eg; // 当前弧优化,更新当前弧
        int to = edges[eg].to, vol = edges[eg].w;
        if (vol > 0 && lv[to] == lv[p] + 1) // 往层数高的方向增广
        {
            int c = dfs(to, min(vol, rmn)); // 尽可能多地传递流量
            rmn -= c; // 剩余流量减少
            edges[eg].w -= c; // 更新残余容量
            edges[eg ^ 1].w += c; // 再次提醒,链式前向星的cnt需要初始化为 1 才能这样求反向边
        }
    }
    return flow - rmn; // 返回传递出去的流量的大小
}
inline int dinic()
{
    int ans = 0;
    while (bfs())
        ans += dfs();
    return ans;
}


    cnt=1;

DSU并查集

struct DSU{
    vector<int>f,siz;
    DSU(int n) : f(n), siz(n,1) {std::iota(f.begin(),f.end(),0);}
    int find(int x){
        while(x!=f[x]) x=f[x]=f[f[x]];
        return x;
    }
    bool same(int x,int y){return find(x)==find(y);}
    bool merge(int x,int y){
        x=find(x);y=find(y);
        if(x==y) return false;
        siz[x]+=siz[y];
        f[y]=x;
        return true;
    }
    int size(int x){return siz[find(x)];}
};
//siz是节点数
//启发式合并
        u=find(u);
        v=find(v);
        if(edge[u].size()<edge[v].size()){
            swap(u,v);
        }
        ans=ans*siz[u]%p*siz[v]%p;
        int cnt=0;
        for(auto t:edge[v]){
            if(find(t)==u){
                cnt++;
            }else{
                edge[u].push_back(t);
            }
        }
        if(cnt!=1){
            ok=0;
            break;
        }
        merge(u,v);



//维护每个点的深度
struct DSU{
    vector<int> fa,deep;
    DSU(int n):fa(n),deep(n){
        iota(begin(fa),end(fa),0);
    }
    int find(int x){
        if(x==fa[x]) return x;
        int tmp=find(fa[x]);
        deep[x]+=deep[fa[x]];
        return fa[x]=tmp;
    }
};

        dsu.fa[v]=u;
        dsu.deep[v]=1;
        dsu.find(v);
        f[dsu.find(u)]=max(f[dsu.find(u)],f[v]+dsu.deep[v]);

排列组合C

    ll C[n+7][n+7];
    for(int i=0;i<=n;i++){
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++){
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%q;
        }
    }

归并排序

void msort(int l,int r){
    if(l==r){
        return ;
    }
    int c[n+1];
    int idx=l;
    int mid=(l+r)/2;
    int i=l,j=mid+1;
    msort(l,mid);
    msort(mid+1,r);
    while(i<=mid&&j<=r){
        if(a[i]<=a[j]){
            c[idx++]=a[i++];
        }else{
            c[idx++]=a[j++];
            // ans+=mid-i+1;
            // ÄæÐòÊý
        }
    }
    while(i<=mid){
        c[idx++]=a[i++];
    }
    while(j<=r){
        c[idx++]=a[j++];
    }
    for(int k=l;k<=r;k++){
        a[k]=c[k];
    }
}

凸包

// DEPENDS eq, lt, cross, V-V, P<P
using Points = vector<Point>;
double theta(Point p) { return p == O ? -1 / 0. : atan2(p.y, p.x); } // 求极角
void psort(Points &ps, Point c = O)                                  // 极角排序
{
    sort(ps.begin(), ps.end(), [&](auto p1, auto p2) {
        return lt(theta(p1 - c), theta(p2 - c));
    });
}
bool check(Point p, Point q, Point r) // 检查三个点组成的两个向量的旋转方向是否为逆时针
{
    return lt(0, cross(q - p, r - q));
}
Points chull(Points &ps)
{
    psort(ps, *min_element(ps.begin(), ps.end())); // 以最左下角的点为极角排序
    Points H{ps[0]};
    for (int i = 1; i < ps.size(); i++)
    {
        while (H.size() > 1 && !check(H[H.size() - 2], H.back(), ps[i]))
            H.pop_back();
        H.push_back(ps[i]);
    }
    return H;
}

树状数组

#define lowbit(x) ((x) & (-x))
//单点修改
int tree[MAXN];
inline void update(int i, int x)
{
    for (int pos = i; pos < MAXN; pos += lowbit(pos))
        tree[pos] += x;
}

//求前n项和
inline int query(int n)
{
    int ans = 0;
    for (int pos = n; pos; pos -= lowbit(pos))
        ans += tree[pos];
    return ans;
}

//区间查询
inline int query(int a, int b)
{
    return query(b) - query(a - 1);
}

序列自动机

//查找子序列

struct node{
    int next[26];
};

    vector<node > G(n+7);
    vector<int > nxt(26,0);
    for (int i = n - 1; i >= 0; --i) {
        nxt[s[i] - 'A'] = i + 2;
        for (int ch = 0; ch < 26; ++ch)
            G[i + 1].next[ch] = nxt[ch];
    }

    string ac="";

    int cur=i;
    for(auto c:ac){
        cur=G[cur].next[c-'A'];
        if(!cur){
            break;
        }
    }


//例题:https://ac.nowcoder.com/acm/contest/78807/E

二进制前缀异或和情况种数

    int pre[30][n+1][2];
    int suff[30][n+2][2];
    memset(pre,0,sizeof(pre));
    memset(suff,0,sizeof(suff));
    for(int i=1;i<=n;i++){
        for(int j=0;j<30;j++){
            int t=(a[i]>>j)&1;
            pre[j][i][0]=pre[j][i-1][t^0]+(t==0);
            pre[j][i][1]=pre[j][i-1][t^1]+(t==1);
        }
    }
    for(int i=n;i>=1;i--){
        for(int j=0;j<30;j++){
            int t=(a[i]>>j)&1;
            suff[j][i][0]=suff[j][i+1][t^0]+(t==0);
            suff[j][i][1]=suff[j][i+1][t^1]+(t==1);
        }
    }


    //第j位第i个数的前缀异或和为1或0的情况种数
    //例题https://codeforces.com/contest/1957/problem/D

威尔逊定理

  • 定义\(C(i,j)\)为从集合{\(1, 2, \ldots, i\)}中选择j个数构成圆排列,有多少个这种圆排列
  • \(C(i,j) \bmod j = \frac{i(i-1)\cdots(i-j+1)}{j} \bmod j = \left( (j-1)! \times \left\lfloor \frac{i}{j} \right\rfloor \right) \bmod j\)
  • (p-1)!%p(p-1的阶乘模p)
  • 威尔逊定理:
    • 如果p是4,结果为2
    • 如果p是质数,结果为p-1

__int128

#define int __int128
inline void read(int &n){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    n=x*f;
}
inline void print(int n){
    if(n<0){
        putchar('-');
        n*=-1;
    }
    if(n>9) print(n/10);
    putchar(n % 10 + '0');
}
#undef int

快读

inline void read(int &n){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    n=x*f;
}
inline void print(int n){
    if(n<0){
        putchar('-');
        n*=-1;
    }
    if(n>9) print(n/10);
    putchar(n % 10 + '0');
}

树的直径(树形DP)

#include<bits/stdc++.h>
using namespace std;
const int Maxn=300000+10,inf=0x3f3f3f3f;
int d[Maxn],g[Maxn];
int f[Maxn],c[Maxn]; // 这里的c数组的含义不是跟上面对应的
bool vis[Maxn];
vector <int> e[Maxn];
int n,m,q,len;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
int find(int x)
{
	if(f[x]==x)return x;
	return f[x]=find(f[x]);
}
void dfs(int x,int fa) // 树形DP求树的直径
{
	int m1=-1,m2=-1;
	for(int i=0;i<e[x].size();++i)
	{
		int y=e[x][i];
		if(y==fa)continue;
		dfs(y,x);
		int tmp=d[y]+1;
		d[x]=max(d[x],tmp);
		if(tmp>m1)m2=m1,m1=tmp;
		else if(tmp>m2)m2=tmp;
	}
	g[x]=max(max(0,m1+m2),max(m1,m2));
	len=max(len,g[x]);
}
void calc(int x) // 寻找树的直径
{
	len=0;
	dfs(x,0);
	c[x]=len;
}
int main()
{
//	freopen("in.txt","r",stdin);
	n=read(),m=read();
	
	for(int i=1;i<=n;++i)
	f[i]=i;
	
	for(int i=1;i<=m;++i)
	{
		int x=read(),y=read();
		f[find(x)]=find(y);
		e[x].push_back(y);
		e[y].push_back(x);
	}
	
	for(int i=1;i<=n;++i)
	{
		if(f[i]!=i || vis[i])continue;
		calc(i);
		vis[i]=1;
	}
	
	return 0;
}

二分图染色

//例题:https://codeforces.com/gym/104901/problem/G
//相邻的两个节点颜色不同

//一个节点有两种状态
//不能同时存在的两种状态用边相连,跑dfs进行判定能否进行二分图染色,若可以,情况种数为\(2^{连通块个数}\)

    vector<int > vis(2*r+1,0);
    int ok=1;
    function<void (int ) > dfs=[&](int u){
        for(auto t:edges[u]){
            if(vis[t]==vis[u]){
                ok=0;
                return ;
            }else if(vis[t]>0){
                continue ;
            }
            if(vis[u]==1){
                vis[t]=2;
            }else{
                vis[t]=1;
            }
            dfs(t);
            if(ok==0){
                return ;
            }
        }
    };
    ll ans=1;
    for(int i=1;i<=2*r;i++){
        if(vis[i]>0){
            continue;
        }
        if(edges[i].size()==0){
            continue;
        }
        vis[i]=1;
        dfs(i);
        if(ok==0){
            break;
        }
        ans=(ans*2)%p;
    }

线性基

vector<ull> B;
void insert(ull x) {
    for (auto b : B)
        x = min(x, b ^ x);
    for (auto &b : B)
        b = min(b, b ^ x);
    if (x)
        B.push_back(x);
}

//第k大的数
    sort(B.begin(), B.end());
    ull ans = 0;
    if (B.size() < n)
        k--;
    for (auto b : B) {
        if (k & 1)
            ans ^= b;
        k >>= 1;
    }
    if (k == 0)
        cout << ans << endl;
    else
        cout << -1 << endl;

    
//一个数是否存在
    bool check(ull x) {
        for (auto b : B)
            x = min(x, b ^ x);
        return x == 0;
    }

//最大异或和就是B中所有数的异或和
ull queryMax(){
    ull x=0;
    for(auto b:B){
        x^=b;
    }
    return x;
}

mex不存在的最小非负整数

int mex(auto v) // v可以是vector、set等容器 
{
    unordered_set<int> S;
    for (auto e : v)
        S.insert(e);
    for (int i = 0;; ++i)
        if (S.find(i) == S.end())
            return i;
}

01-Trie

const int MAXN = 3200000, MAXBIT = 31;
int nex[MAXN][2], cnt;
int num[MAXN];

int mx[MAXN];

void init()
{
    /*
    memset(nex, 0, sizeof(nex));
    memset(num, 0, sizeof(num));*/
    for(int i=1;i<=cnt;i++){
        nex[i][0]=nex[i][1]=0;
        num[i]=0;
    }
    cnt = 1;
}
void insert(int n)
{
    int cur = 1;
    for (int i = MAXBIT; i >= 0; --i)
    {
        int bit = n >> i & 1; // 求出当前位并插入
        if (!nex[cur][bit])
            nex[cur][bit] = ++cnt;
        cur = nex[cur][bit];
    }
    num[cur] = n;
}
int find_max(int x) // 找到与x异或最大的那个数
{
    int cur = 1;
    for (int i = MAXBIT; i >= 0; --i)
    {
        int bit = x >> i & 1;
        if (nex[cur][bit ^ 1]) // 优先走与当前位不同的路径
            cur = nex[cur][bit ^ 1];
        else
            cur = nex[cur][bit];
    }
    return x ^ num[cur];
}

bool query(int x,int k,int val){
    int p=1;
    int res=0;
    for(int i=30;i>=0;--i){
        int t=(x>>i)&1;
        int res=(k>>i)&1;
        if(res==1){
            if(nex[p][t^1]){
                p=nex[p][t^1];
            }else{
                return 0;
            }
        }else{
            if(nex[p][t^1]){
                if(mx[nex[p][t^1]]>=val){
                    return 1;
                }
            }
            if(nex[p][t]){
                p=nex[p][t];
            }
        }
    }
    return mx[p]>=val;
}

Trie字典树(前缀树)

const int MAXN = 500005;
int nex[MAXN][26], cnt; // 用类似链式前向星的方式存图,next[i][c]表示i号点所连、存储字符为c+'a'的点的编号
void init() // 初始化
{
    memset(nex, 0, sizeof(nex)); // 全部重置为0,表示当前点没有存储字符
    /*
    for(int i=1;i<=cnt;i++){
        for(int j=0;j<26;j++){
            nex[i][j]=0;
        }
    }*/
    cnt = 1;
}
void insert(const string &s) // 插入字符串
{
    int cur = 1;
    for (auto c : s)
    {
        // 尽可能重用之前的路径,如果做不到则新建节点
        if (!nex[cur][c - 'a']) 
            nex[cur][c - 'a'] = ++cnt; 
        cur = nex[cur][c - 'a']; // 继续向下
    }
}
bool find_prefix(const string &s) // 查找某个前缀是否出现过
{
    int cur = 1;
    for (auto c : s)
    {
        // 沿着前缀所决定的路径往下走,如果中途发现某个节点不存在,说明前缀不存在
        if (!nex[cur][c - 'a'])
            return false;
        cur = nex[cur][c - 'a'];
    }
    return true;
}

lucas卢卡斯定理O(p+logp)

// 需要先预处理出fact[],即阶乘
inline ll C(ll m, ll n, ll p)
{
    return m < n ? 0 : fact[m] * inv(fact[n], p) % p * inv(fact[m - n], p) % p;
}
inline ll lucas(ll m, ll n, ll p)
{
    return n == 0 ? 1 % p : lucas(m / p, n / p, p) * C(m % p, n % p, p) % p;
}

区间dp

    vector< vector<ll> > dp(2 * n + 1,vector<ll>(2 * n + 1, 0ll));//dp[2*n+1][2*n+1]初始化为0
    for(int i=1;i<=2*n;i++){//枚举终点
        for(int j=i-1;j>=1;j--){//枚举起点
            if(i-j+1>n){//是否符合题意
                break;
            }
            dp[j][i]=dp[j+1][i];//初始化或继承
            for(int k=0;k<edge[j].size();k++){//枚举分界点
                ll cnt;//由分界点划分出的各区间dp的和
                dp[j][i]=max(dp[j][i],cnt);
            }
        }
    }

最近公共祖先LCA

bool dfs(TreeNode* k,TreeNode* p,TreeNode* q){
    if(k==NULL){
        return false;
    }
    bool lson=dfs(k->left,p,q);
    bool rson=dfs(k->right,p,q);
    if(lson==1&&rson==1||(k==p||k==q)&&(lson==1||rson==1)){
        ans=k;
    }
    return k==p||k==q||lson==1||rson==1;
}
//O(n)

//

int Log2[MAXN], fa[MAXN][20], dep[MAXN]; // fa的第二维大小不应小于log2(MAXN)
bool vis[MAXN];
void dfs(int cur, int fath = 0)
{
    if (vis[cur])
        return;
    vis[cur] = true;
    dep[cur] = dep[fath] + 1;
    fa[cur][0] = fath;
    for (int i = 1; i <= Log2[dep[cur]]; ++i)
        fa[cur][i] = fa[fa[cur][i - 1]][i - 1];
    for (int eg = head[cur]; eg != 0; eg = edges[eg].next)
        dfs(edges[eg].to, cur);
}
int lca(int a, int b)
{
    if (dep[a] > dep[b])
        swap(a, b);
    while (dep[a] != dep[b])
        b = fa[b][Log2[dep[b] - dep[a]]];
    if (a == b)
        return a;
    for (int k = Log2[dep[a]]; k >= 0; k--)
        if (fa[a][k] != fa[b][k])
            a = fa[a][k], b = fa[b][k];
    return fa[a][0];
}

//


//预处理O(nlogn),查找O(logn)

int fa[MXN][31], cost[MXN][31], dep[MXN];
int n, m;
int a, b, c;

// dfs,用来为 lca 算法做准备。接受两个参数:dfs 起始节点和它的父亲节点。
void dfs(int root, int fno) {
  // 初始化:第 2^0 = 1 个祖先就是它的父亲节点,dep 也比父亲节点多 1。
  fa[root][0] = fno;
  dep[root] = dep[fa[root][0]] + 1;
  // 初始化:其他的祖先节点:第 2^i 的祖先节点是第 2^(i-1) 的祖先节点的第
  // 2^(i-1) 的祖先节点。
  for (int i = 1; i < 31; ++i) {
    fa[root][i] = fa[fa[root][i - 1]][i - 1];
    cost[root][i] = cost[fa[root][i - 1]][i - 1] + cost[root][i - 1];
  }
  // 遍历子节点来进行 dfs。
  int sz = v[root].size();
  for (int i = 0; i < sz; ++i) {
    if (v[root][i] == fno) continue;
    cost[v[root][i]][0] = w[root][i];
    dfs(v[root][i], root);
  }
}

// lca。用倍增算法算取 x 和 y 的 lca 节点。
int lca(int x, int y) {
  // 令 y 比 x 深。
  if (dep[x] > dep[y]) swap(x, y);
  // 令 y 和 x 在一个深度。
  int tmp = dep[y] - dep[x], ans = 0;
  for (int j = 0; tmp; ++j, tmp >>= 1)
    if (tmp & 1) ans += cost[y][j], y = fa[y][j];
  // 如果这个时候 y = x,那么 x,y 就都是它们自己的祖先。
  if (y == x) return ans;
  // 不然的话,找到第一个不是它们祖先的两个点。
  for (int j = 30; j >= 0 && y != x; --j) {
    if (fa[x][j] != fa[y][j]) {
      ans += cost[x][j] + cost[y][j];
      x = fa[x][j];
      y = fa[y][j];
    }
  }
  // 返回结果。
  ans += cost[x][0] + cost[y][0];
  return ans;
}

完全背包dp

	int t;
    cin>>t;
    int n;
    cin>>n;
    int dp[t+1];//dp[i]表示容量i最多可以装多少卡路里的食物
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++){
        int a,b;//a为体积或重量,b为卡路里
        cin>>a>>b;
        for(int j=t;j>=1;j--){//j--表示一种食物只能选择一次,j++表示一种食物可以选择多次
            if(j>=a){
                dp[j]=max(dp[j],dp[j-a]+b);
            }
        }
    }

单调队列

deque<int> Q; // 存储的是编号
for (int i = 0; i < n; ++i)
{
    if (!Q.empty() && i - Q.front() >= m) // 毕业
        Q.pop_front();
    while (!Q.empty() && V[Q.back()] < V[i]) // 比新生弱的当场退役(求区间最小值把这里改成>即可)
        Q.pop_back();
    Q.push_back(i); // 新生入队
    if (i >= m - 1)
        cout << V[Q.front()] << " ";
}

最长公共子串

while (cin >> s1 >> s2)
{
    memset(dp, 0, sizeof(dp));
    int n1 = strlen(s1), n2 = strlen(s2), ma = 0;
    for (int i = 1; i <= n1; ++i)
        for (int j = 1; j <= n2; ++j)
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = 0;
    for (int i = 1; i <= n1; ++i)
        for (int j = 1; j <= n2; ++j)
            ma = max(ma, dp[i][j]);
    cout << ma << endl;
}

最长公共子序列LCS

const int MAXN = 505;
char s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int lcs(int n1, int n2)
{
    if (dp[n1][n2] != -1)
        return dp[n1][n2];
    if (n1 == 0 || n2 == 0)
        dp[n1][n2] = 0;
    else if (s1[n1 - 1] == s2[n2 - 1])
        dp[n1][n2] = lcs(n1 - 1, n2 - 1) + 1;
    else
        dp[n1][n2] = max(lcs(n1, n2 - 1), lcs(n1 - 1, n2));
    return dp[n1][n2];
}


//精简版
while (cin >> s1 >> s2)
{
    memset(dp, 0, sizeof(dp));
    int n1 = strlen(s1), n2 = strlen(s2);
    for (int i = 1; i <= n1; ++i)
        for (int j = 1; j <= n2; ++j)
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    cout << dp[n1][n2] << endl;
}

欧拉函数、欧拉筛

//小于(或不大于,这里是一样的n但与n互质的正整数的数量 O(logn)
int phi(int n)
{
    int res = n;
    for (int i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
            res = res / i * (i - 1); // 先除再乘防止溢出
        while (n % i == 0) // 每个质因数只处理一次,可以把已经找到的质因数除干净
            n /= i;
    }
    if (n > 1)
        res = res / n * (n - 1); // 最后剩下的部分也是原来的n的质因数
    return res;
}


//求1-n的欧拉函数 O(nloglogn)
int phi[MAXN];
void init(int n)
{
    for (int i = 1; i <= n; i++)
        phi[i] = i; // 除1外没有数的欧拉函数是本身,所以如果phi[i] = i则说明未被筛到
    for (int i = 2; i <= n; i++)
        if (phi[i] == i) // 未被筛到
            for (int j = i; j <= n; j += i) // 所有含有该因子的数都进行一次操作
                phi[j] = phi[j] / i * (i - 1);
}



//欧拉筛 O(n)
int phi[MAXN];
bool isnp[MAXN];
vector<int> primes; // 质数表
void init(int n)
{
    phi[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (!isnp[i])
            primes.push_back(i), phi[i] = i - 1; // 性质一,指数为1的情形
        for (int p : primes)
        {
            if (p * i > n)
                break;
            isnp[p * i] = 1;
            if (i % p == 0)
            {
                phi[p * i] = phi[i] * p; // 性质二
                break;
            }
            else
                phi[p * i] = phi[p] * phi[i]; // 这时肯定互质,用性质三
        }
    }
}

拓扑排序O(n)

// deg是入度,在存图的时候需要录入数据
// A是排序后的数组
vector <int > edges[MAXN];
int deg[MAXN], A[MAXN];
bool toposort(int n)
{
    int cnt = 0;
    queue<int> q;
    for (int i = 1; i <= n; ++i)
        if (deg[i] == 0)
            q.push(i);
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        A[cnt++] = t;
        for (auto to : edges[t])
        {
            deg[to]--;
            if (deg[to] == 0) // 出现了新的入度为0的点
                q.push(to);
        }
    }
    return cnt == n;
}

最长上升子序列LIS dp O(nlogn)

int len = 0;
for (int i = 0; i < n; ++i)
{
    if (dp[len] < a[i])
        dp[++len] = a[i];
    else
        *lower_bound(dp + 1, dp + len + 1, a[i]) = a[i];
}

/*最长下降子序列
    int dp[n+1];
    memset(dp,0,sizeof(dp));
    int len=0;
    dp[len]=INF;
    for(int i=0;i<n;i++){
        if(a[i]<dp[len]){
            dp[++len]=a[i];
        }else{
            *lower_bound(dp+1,dp+1+len,a[i],greater<int >())=a[i];
        }
    }
*/

set小用法

// 现存可用的元素
set<int> available;
// 需要大于等于的值
int x;

// 查找最小的大于等于x的元素
set<int>::iterator it = available.lower_bound(x);
if (it == available.end()) {
  // 不存在这样的元素,则进行相应操作……
} else {
  // 找到了这样的元素,将其从现存可用元素中移除
  available.erase(it);
  // 进行相应操作……
}

tarjan强连通图

stack<int> stk;
// instk:是否在栈中  scc:每个点所属的强连通分量编号  cscc:强连通分量的数量
int dfsn[MAXN], low[MAXN], instk[MAXN], scc[MAXN], cnt, cscc;
void tarjan(int p)
{
    low[p] = dfsn[p] = ++cnt;
    instk[p] = 1;
    stk.push(p); // 进栈
    for (auto q : edges[p])
    {
        if (!dfsn[q]) // 未访问过
        {
            tarjan(q); // 递归地搜索
            low[p] = min(low[p], low[q]);
        }
        else if (instk[q]) // 访问过,且q可达p
            low[p] = min(low[p], dfsn[q]);
    }
    if (low[p] == dfsn[p]) // 发现强连通分量的根
    {
        int top;
        cscc++;
        do
        {
            top = stk.top();
            stk.pop();
            instk[top] = 0;
            scc[top] = cscc; // 记录所属的强连通分量
        } while (top != p); // 直到弹出p才停止
    }
}

Dijkstra最短路O(Elogv)

const int max_V=1e5+7;

struct edge { int to, cost; };
typedef pair <int ,int > P;//first是最短距离,second是顶点的编号

int V;
vector <edge> G[max_V];
int d[max_V];

void dijkstra(int s){
    priority_queue<P, vector<P>, greater<P> > que;
    fill(d,d+V+1,INF);
    d[s]=0;
    que.push(P(0,s));

    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first){
            continue;
        }
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost){
                d[e.to]=d[v]+e.cost;
                que.push(P(d[e.to],e.to));
            }
        }
    }
}

Bellman-Ford最短路O(nm)

int INF = 0x3f3f3f3f;

struct edge { int from, to, cost; }edges[12405];

int n,m;

edge es[1000];

int d[2510];


void shortest_path(int s){
	for(int i=0;i<=n;i++){
		d[i]=INF;
	}
	d[s]=0;
	while(true){
		bool update=false;
		for(int i=0;i<m*2;i++){
			if(d[edges[i].from]!=INF&&d[edges[i].to]>d[edges[i].from]+edges[i].cost){
				d[edges[i].to]=d[edges[i].from]+edges[i].cost;
				update=true;
			}
		}
		if(!update){
			break;
		}
	}
}

	for(i=0;i<m;i++){
		cin>>u>>v>>w;
		edges[i*2].from=u;
		edges[i*2].to=v;
		edges[i*2].cost=w;
		edges[i*2+1].from=v;
		edges[i*2+1].to=u;
		edges[i*2+1].cost=w;
		
	}
	shortest_path(s);
	cout<<d[t]<<endl;
	

Floyd-Warshall最短路O(n^3)

int INF = 0x3f3f3f3f;

int d[2510][2510];
int n,m; 

void warshall_floyd(){
	for(int k=0;k<=n;k++){
		for(int i=0;i<=n;i++){
			for(int j=0;j<=n;j++){
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
			}
		}
	}
}

	for(i=0;i<=n;i++){
		for(j=i+1;j<=n;j++){
			d[i][j]=INF;
			d[j][i]=INF;
		}
	}
	for(i=0;i<m;i++){
		cin>>u>>v>>w;
		d[u][v]=w;
		d[v][u]=w;
	}
	warshall_floyd();
	cout<<d[s][t]<<endl;

gcdO(log a)

int gcd(int a,int b){
	if(b==0) return a;
	return gcd(b,a%b);
}

mod_powO(log n)

ll mod_pow(ll x,ll n,ll mod){
	if(n==0) return 1;
	ll res=mod_pow(x*x%mod,n/2,mod);
	if(n&1) res=res*x%mod;
	return res;
}

STL

//vector容器 
	vector<pair<ll,ll>> a;
	for(int i=1;i<=n;i++){
		ll c,b;
		cin>>c;
		b=i;
		a.push_back(make_pair(c,b));
	}
	sort(a.begin(),a.end());
	for(auto p:a){
		//p为a数组的值 
	}
	//
	vector <vector<int >> res(k+1);//res[k+1][]
	for(int i=1;i<=n;i++){
		cin>>a[i];
		res[a[i]].push_back(i);
	}
	for(int i=0;i<res.size();i++){
		cout<<res[i]<<endl;
	}
	res[i].empty()//true为空,false为非空
	//
	vector <string > s;
	string str;
	cin>>str;
	s.push_back(str);
	

//map容器(可做桶用 
	map<string,bool> s1;
	string str;
	cin>>str;
	s1[str]=1;

并查集

const int max_N=1e5+7;

int par[max_N];
int rank[max_N];
int value[max_N];
//初始化 max_N个元素 
void init(int n){
	if(n==max_N) n--;
	for(int i=0;i<=n;i++){
		par[i]=i;
		rank[i]=0;
		value[i]=1;
	}
}
//查询树的根 
int find(int x){
	if(par[x]==x){
		return x;
	}else{
		return par[x]=find(par[x]);
	}
}
//合并x和y所属的集合 
void unite(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y) return ;
	if(rank[x]<rank[y]){
		par[x]=y;
		value[y]+=value[x];
	}else{
		par[y]=x;
		value[x]+=value[y];
		if(rank[x]==rank[y]) rank[x]++;
	}
}
//判断x和y是否属于同一个集合
bool same(int x,int y){
	return find(x)==find(y);
}

查找子串find()

std::string s = "This is a sample string. Sample strings are useful for samples.";
std::string str = "sample";

size_t pos = s.find(str);
int count = 0;

while (pos != std::string::npos) {
    count++;
    pos = s.find(str, pos + 1);
}

二维差分

// 给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c
void add(int x1, int y1, int x2, int y2, int c)
{
    f[x1][y1] += c;
    f[x2 + 1][y1] -= c;
    f[x1][y2 + 1] -= c;
    f[x2 + 1][y2 + 1] += c;
}

高精度

const int LEN = 1004;

void clear(int a[]) {
  for (int i = 0; i < LEN; ++i) a[i] = 0;
}

void read(int a[]) {
  static char s[LEN + 1];
  scanf("%s", s);

  clear(a);

  int len = strlen(s);
  // 如上所述,反转
  for (int i = 0; i < len; ++i) a[len - i - 1] = s[i] - '0';
  // s[i] - '0' 就是 s[i] 所表示的数码
  // 有些同学可能更习惯用 ord(s[i]) - ord('0') 的方式理解
}

void print(int a[]) {
  int i;
  for (i = LEN - 1; i >= 1; --i)
    if (a[i] != 0) break;
  for (; i >= 0; --i) putchar(a[i] + '0');
  putchar('\n');
}

void add(int a[], int b[], int c[]) {
  clear(c);

  // 高精度实现中,一般令数组的最大长度 LEN 比可能的输入大一些
  // 然后略去末尾的几次循环,这样一来可以省去不少边界情况的处理
  // 因为实际输入不会超过 1000 位,故在此循环到 LEN - 1 = 1003 已经足够
  for (int i = 0; i < LEN - 1; ++i) {
    // 将相应位上的数码相加
    c[i] += a[i] + b[i];
    if (c[i] >= 10) {
      // 进位
      c[i + 1] += 1;
      c[i] -= 10;
    }
  }
}

void sub(int a[], int b[], int c[]) {
  clear(c);

  for (int i = 0; i < LEN - 1; ++i) {
    // 逐位相减
    c[i] += a[i] - b[i];
    if (c[i] < 0) {
      // 借位
      c[i + 1] -= 1;
      c[i] += 10;
    }
  }
}

void mul_short(int a[], int b, int c[]) {
  clear(c);

  for (int i = 0; i < LEN - 1; ++i) {
    // 直接把 a 的第 i 位数码乘以乘数,加入结果
    c[i] += a[i] * b;

    if (c[i] >= 10) {
      // 处理进位
      // c[i] / 10 即除法的商数成为进位的增量值
      c[i + 1] += c[i] / 10;
      // 而 c[i] % 10 即除法的余数成为在当前位留下的值
      c[i] %= 10;
    }
  }
}

void mul(int a[], int b[], int c[]) {
  clear(c);

  for (int i = 0; i < LEN - 1; ++i) {
    // 这里直接计算结果中的从低到高第 i 位,且一并处理了进位
    // 第 i 次循环为 c[i] 加上了所有满足 p + q = i 的 a[p] 与 b[q] 的乘积之和
    // 这样做的效果和直接进行上图的运算最后求和是一样的,只是更加简短的一种实现方式
    for (int j = 0; j <= i; ++j) c[i] += a[j] * b[i - j];

    if (c[i] >= 10) {
      c[i + 1] += c[i] / 10;
      c[i] %= 10;
    }
  }
}

// 被除数 a 以下标 last_dg 为最低位,是否可以再减去除数 b 而保持非负
// len 是除数 b 的长度,避免反复计算
bool greater_eq(int a[], int b[], int last_dg, int len) {
  // 有可能被除数剩余的部分比除数长,这个情况下最多多出 1 位,故如此判断即可
  if (a[last_dg + len] != 0) return true;
  // 从高位到低位,逐位比较
  for (int i = len - 1; i >= 0; --i) {
    if (a[last_dg + i] > b[i]) return true;
    if (a[last_dg + i] < b[i]) return false;
  }
  // 相等的情形下也是可行的
  return true;
}

void div(int a[], int b[], int c[], int d[]) {
  clear(c);
  clear(d);

  int la, lb;
  for (la = LEN - 1; la > 0; --la)
    if (a[la - 1] != 0) break;
  for (lb = LEN - 1; lb > 0; --lb)
    if (b[lb - 1] != 0) break;
  if (lb == 0) {
    puts("> <");
    return;
  }  // 除数不能为零

  // c 是商
  // d 是被除数的剩余部分,算法结束后自然成为余数
  for (int i = 0; i < la; ++i) d[i] = a[i];
  for (int i = la - lb; i >= 0; --i) {
    // 计算商的第 i 位
    while (greater_eq(d, b, i, lb)) {
      // 若可以减,则减
      // 这一段是一个高精度减法
      for (int j = 0; j < lb; ++j) {
        d[i + j] -= b[j];
        if (d[i + j] < 0) {
          d[i + j + 1] -= 1;
          d[i + j] += 10;
        }
      }
      // 使商的这一位增加 1
      c[i] += 1;
      // 返回循环开头,重新检查
    }
  }
}

树上dp

#include <bits/stdc++.h>
using namespace std ;
const int max_N=2e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;

vector<int> g[max_N];
ll dp[max_N];

void dfs(int u,int fa){
	if(g[u].size()==1&&u-1){
		dp[u]=1;
	}
	for(auto v : g[u]){
		if(v==fa){
			continue ;
		}
		dfs(v,u);
		dp[u]+=dp[v];
	}
}

void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		g[i].clear();
		dp[i]=0;
	}
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		g[a].push_back(b);
		g[b].push_back(a);
	}
	dfs(1,0);
	int q;
	cin>>q;
	for(int i=1;i<=q;i++){
		int x,y;
		cin>>x>>y;
		cout<<dp[x]*dp[y]<<endl;
	}
}

int main(){
	int t;
	
	cin>>t;
	while(t--){
		solve();
	}
	
	return 0;
}

位运算符

0&0=0;
0&1=0;
1&0=0;
1&1=1;
//全1出1
0|0=0;
0|1=1;
1|0=1;
1|1=1;
//有1出1
0^0=0;
0^1=1;
1^0=1;
1^1=0;
//一样出0,不一样出1

逆元(除数取模)

//拓展欧几里得

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;
}
ll inv(ll a, ll p)
{
    ll x, y;
    if (exgcd(a, p, x, y) != 1) // 无解的情形
        return -1;
    return (x % p + p) % p;
}

//inv(a,p)就是取模p意义下的1/a;


//费马小定理
inline ll qpow(ll a, ll n, ll p)// 快速幂
{
    ll ans = 1;
    while (n)
    {
        if (n & 1)
            ans = ans % p * a % p;
        a = a % p * a % p;
        n >>= 1;
    }
    return ans;
}
inline ll inv(ll a, ll p)
{
    return qpow(a, p - 2, p);
}

三维bfs

char g[max_N][max_N][max_N];

const int   dx[]={0,0,0,0,-1,1},
            dy[]={0,0,-1,1,0,0},
            dz[]={-1,1,0,0,0,0};
struct node
{
    int x,y,z;
};
int l,r,c;
queue<node>q;
int d[max_N][max_N][max_N];
int bfs(node st){
    while(!q.empty()){
        q.pop();
    }
    q.push(st);
    d[st.x][st.y][st.z]=0;
    while(!q.empty()){
        node t=q.front();
        q.pop();
        g[t.x][t.y][t.z]='#';
        for(int i=0;i<6;i++){
            int x=t.x+dx[i],y=t.y+dy[i],z=t.z+dz[i];
            if(x<0||x>=l||y<0||y>=r||z<0||z>=c){
                continue;
            }
            if(g[x][y][z]=='#'){
                continue;
            }
            d[x][y][z]=d[t.x][t.y][t.z]+1;
            if(g[x][y][z]=='E'){
                return d[x][y][z];
            }
            g[x][y][z]='#';
            node tt;
            tt.x=x,tt.y=y,tt.z=z;
            q.push(tt);
        }
    }
    return 0;
}

快速幂

#include <iostream>
long long Quick_Power(long long x,long long n)
{
    if(n==0)
    {
        return 1;
    }
    else if(n%2==1)
    {
        return Quick_Power(x,n-1)*x;
    }
    else
    {
        long long tmp=Quick_Power(x,n/2);
        return tmp*tmp;
    }
}
using namespace std;
int main()
{
    long long x,n;
    cin>>x>>n;
    cout<<Quick_Power(x,n);
}



long long ksm(long long a, long long b)
{ 
    long long res = 1; 
    while(b) 
    { 
        if(b & 1)  
            res = res * a % M; 
            a = a * a % M; 
            b >>= 1; 
    } 
    return res; 
}

tarjan强连通

void tarjan(int now)
{
    dfn[now]=low[now]=++cnt;  //初始化
    stack[++t]=now;       //入栈操作
    v[now]=1;            //v[]代表该点是否已入栈
    for(int i=f[now];i!=-1;i=e[i].next)  //邻接表存图
        if(!dfn[e[i].v])           //判断该点是否被搜索过
        {
            tarjan(e[i].v);
            low[now]=min(low[now],low[e[i].v]); //回溯时更新low[ ],取最小值
        }
        else if(v[e[i].v])
            low[now]=min(low[now],dfn[e[i].v]); //一旦遇到已入栈的点,就将该点作为连通量的根
                             //这里用dfn[e[i].v]更新的原因是:这个点可能
                             //已经在另一个强连通分量中了但暂时尚未出栈,所
                             //以now不一定能到达low[e[i].v]但一定能到达
                             //dfn[e[i].v].
    if(dfn[now]==low[now])
    {
        int cur;
        do
        {
            cur=stack[t--];
            v[cur]=false;                       //不要忘记出栈
        }while(now!=cur);
    }
}
posted @ 2023-10-10 16:24  Beater_1117  阅读(188)  评论(0)    收藏  举报