图论模板

转自:yang_bro

//无向图求割点
int pre[maxn], low[maxn], dfs_clock;
vector<int> G[maxn];
int iscut[maxn];

int dfs(int u, int fa)     //求出所有点i是否为割点iscut[i]
{
    int lowu = pre[u] = ++dfs_clock;
    int child = 0;
    for(int i=0; i<G[u].size(); i++)
    {
        int v = G[u][i];
        if(!pre[v])
        {
            child++;
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if(lowv >= pre[u])  iscutu] = true; //iscut[u] ++;
        }
        else if(pre[v] < pre[u] && v != fa)     lowu = min(lowu, pre[v]);
    }
    //if(fa < 0)  iscut[u]--;
    if(fa < 0 && child == 1)    iscut=false; //iscut[u] = 0;
    low[u] = lowu;
    return lowu;
}
void solve()
{
    memset(pre, 0, sizeof(pre));
    memset(iscut, 0, sizeof(iscut));
    REP(i, n) REP(i, n) if(!pre[i]) dfs(i, -1);
    //iscut若为int数组 使用注释中的代码
    //iscut[i]+1为删除i点后原图中连通分量个数
}

//无向图的双连通分量
int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt; // 割顶的bccno无意义
struct Edge { int u, v; };
vector<int> G[maxn], bcc[maxn];
stack<Edge> S;

int dfs(int u, int fa)
{
  int lowu = pre[u] = ++dfs_clock;
  int child = 0;
  for(int i = 0; i < G[u].size(); i++) {
    int v = G[u][i];
    Edge e = (Edge){u, v};
    if(!pre[v]) { // 没有访问过v
      S.push(e);
      child++;
      int lowv = dfs(v, u);
      lowu = min(lowu, lowv); // 用后代的low函数更新自己
      if(lowv >= pre[u]) {
        iscut[u] = true;
        bcc_cnt++; bcc[bcc_cnt].clear();
        for(;;) {
          Edge x = S.top(); S.pop();
          if(bccno[x.u] != bcc_cnt) { bcc[bcc_cnt].push_back(x.u); bccno[x.u] = bcc_cnt; }
          if(bccno[x.v] != bcc_cnt) { bcc[bcc_cnt].push_back(x.v); bccno[x.v] = bcc_cnt; }
          if(x.u == u && x.v == v) break;
        }
      }
    }
    else if(pre[v] < pre[u] && v != fa) {
      S.push(e);
      lowu = min(lowu, pre[v]); // 用反向边更新自己
    }
  }
  if(fa < 0 && child == 1) iscut[u] = 0;
  return lowu;
}

void find_bcc(int n) {
  // 调用结束后S保证为空,所以不用清空
  memset(pre, 0, sizeof(pre));
  memset(iscut, 0, sizeof(iscut));
  memset(bccno, 0, sizeof(bccno));
  dfs_clock = bcc_cnt = 0;
  for(int i = 0; i < n; i++)
    if(!pre[i]) dfs(i, -1);
};

//无向图求桥 & 双连通分量
int dfs(int u, int fa)
{
    int lowu = pre[u] = ++dfs_clock;
    int nc = G[u].size();
    REP(i, nc)
    {
        int v = edges[G[u][i]].to;
        if(!pre[v])
        {
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if(lowv > pre[u]) edges[G[u][i]].flag = 1, edges[G[u][i]^1].flag = 1; //标记所有桥
        }
        else if(pre[v] < pre[u] && v != fa) lowu = min(lowu, pre[v]);
    }
    return low[u] = lowu;
}

void dfs1(int u)
{
    bccno[u] = bcc_cnt;
    int nc = G[u].size();
    REP(i, nc)
    {
        int v = edges[G[u][i]].to;
        if(!bccno[v] && edges[G[u][i]].flag != 1) dfs1(v);//不经过桥
    }
}

void find_bcc()
{
    CLR(pre, 0); CLR(bccno, 0);
    dfs_clock = bcc_cnt = 0;
    REP(i, n) if(!pre[i]) dfs(i, -1);
    REP(i, n) if(!bccno[i]) bcc_cnt++, dfs1(i);
}


//有向图的强连通分量
vector<int> G[maxn];
int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
stack<int> S;

void dfs(int u) {
  pre[u] = lowlink[u] = ++dfs_clock;
  S.push(u);
  for(int i = 0; i < G[u].size(); i++) {
    int v = G[u][i];
    if(!pre[v]) {
      dfs(v);
      lowlink[u] = min(lowlink[u], lowlink[v]);
    } else if(!sccno[v]) {
      lowlink[u] = min(lowlink[u], pre[v]);
    }
  }
  if(lowlink[u] == pre[u]) {
    scc_cnt++;
    for(;;) {
      int x = S.top(); S.pop();
      sccno[x] = scc_cnt;
      if(x == u) break;
    }
  }
}

void find_scc(int n)
{
  dfs_clock = scc_cnt = 0;
  memset(sccno, 0, sizeof(sccno));
  memset(pre, 0, sizeof(pre));
  for(int i = 0; i < n; i++)
    if(!pre[i]) dfs(i);
};

//无向图的欧拉回路 保存在G中
void add(int u, int v)
{
    g[u][v] = g[v][u] = 1;
    degree[u]++, degree[v]++;
}

void Euler()
{
    FF(i, 1, n+1) if(degree[i])
    {
        int u = i;
        while(true)
        {
            FF(j, 1, n+1) if(g[u][j] && g[j][u])
            {
                g[j][u] = 0;
                degree[u]--, degree[i]--;
                u = j;
                break;
            }
            if(u == i) break;
        }
    }
}

//2-sat dfs版本
struct TwoSAT {
  int n;
  vector<int> G[maxn*2];
  bool mark[maxn*2];
  int S[maxn*2], c;

  bool dfs(int x) {
    if (mark[x^1]) return false;
    if (mark[x]) return true;
    mark[x] = true;
    S[c++] = x;
    for (int i = 0; i < G[x].size(); i++)
      if (!dfs(G[x][i])) return false;
    return true;
  }

  void init(int n) {
    this->n = n;
    for (int i = 0; i < n*2; i++) G[i].clear();
    memset(mark, 0, sizeof(mark));
  }

  // x = xval or y = yval
  void add_clause(int x, int xval, int y, int yval) {
    x = x * 2 + xval;
    y = y * 2 + yval;
    G[x^1].push_back(y);
    G[y^1].push_back(x);
  }

  bool solve() {
    for(int i = 0; i < n*2; i += 2)
      if(!mark[i] && !mark[i+1]) {
        c = 0;
        if(!dfs(i)) {
          while(c > 0) mark[S[--c]] = false;
          if(!dfs(i+1)) return false;
        }
      }
    return true;
  }
};

//堆优化的Dijkstra
struct Edge {
  int from, to, dist;
};

struct HeapNode {
  int d, u;
  bool operator < (const HeapNode& rhs) const {
    return d > rhs.d;
  }
};

struct Dijkstra {
  int n, m;
  vector<Edge> edges;
  vector<int> G[maxn];
  bool done[maxn];    // 是否已永久标号
  int d[maxn];        // s到各个点的距离
  int p[maxn];        // 最短路中的上一条弧

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void AddEdge(int from, int to, int dist) {
    edges.push_back((Edge){from, to, dist});
    m = edges.size();
    G[from].push_back(m-1);
  }

  void dijkstra(int s) {
    priority_queue<HeapNode> Q;
    for(int i = 0; i < n; i++) d[i] = INF;
    d[s] = 0;
    memset(done, 0, sizeof(done));
    Q.push((HeapNode){0, s});
    while(!Q.empty()) {
      HeapNode x = Q.top(); Q.pop();
      int u = x.u;
      if(done[u]) continue;
      done[u] = true;
      for(int i = 0; i < G[u].size(); i++) {
        Edge& e = edges[G[u][i]];
        if(d[e.to] > d[u] + e.dist) {
          d[e.to] = d[u] + e.dist;
          p[e.to] = G[u][i];
          Q.push((HeapNode){d[e.to], e.to});
        }
      }
    }
  }

  // dist[i]为s到i的距离,paths[i]为s到i的最短路径(经过的结点列表,包括s和t)
  void GetShortestPaths(int s, int* dist, vector<int>* paths) {
    dijkstra(s);
    for(int i = 0; i < n; i++) {
      dist[i] = d[i];
      paths[i].clear();
      int t = i;
      paths[i].push_back(t);
      while(t != s) {
        paths[i].push_back(edges[p[t]].from);
        t = edges[p[t]].from;
      }
      reverse(paths[i].begin(), paths[i].end());
    }
  }
};

//spfa判负环
struct Edge {
  int from, to;
  double dist;
};

struct spfa {
  int n, m;
  vector<Edge> edges;
  vector<int> G[maxn];
  bool inq[maxn];     // 是否在队列中
  double d[maxn];     // s到各个点的距离
  int p[maxn];        // 最短路中的上一条弧
  int cnt[maxn];      // 进队次数

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void AddEdge(int from, int to, double dist) {
    edges.push_back((Edge){from, to, dist});
    m = edges.size();
    G[from].push_back(m-1);
  }

  bool negativeCycle() {
    queue<int> Q;
    memset(inq, 0, sizeof(inq));
    memset(cnt, 0, sizeof(cnt));
    for(int i = 0; i < n; i++) { d[i] = 0; inq[0] = true; Q.push(i); }

    while(!Q.empty()) {
      int u = Q.front(); Q.pop();
      inq[u] = false;
      for(int i = 0; i < G[u].size(); i++) {
        Edge& e = edges[G[u][i]];
        if(d[e.to] > d[u] + e.dist) {
          d[e.to] = d[u] + e.dist;
          p[e.to] = G[u][i];
          if(!inq[e.to]) { Q.push(e.to); inq[e.to] = true; if(++cnt[e.to] > n) return true; }
        }
      }
    }
    return false;
  }
};


//kruskal求次小生成树 maxcost[i][j]为i->j的瓶颈路
//对于MST边u, v maxcost[u][v] = 0
int n, m, x[maxn], y[maxn], p[maxn];
int pa[maxn];
int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; }
//G保存MST C保存MST边权
vector<int> G[maxn];
vector<double> C[maxn];
struct Edge {
  int x, y;
  double d;
  bool operator < (const Edge& rhs) const {
    return d < rhs.d;
  }
};

Edge e[maxn*maxn];
double maxcost[maxn][maxn];
vector<int> nodes;

void dfs(int u, int fa, double facost) {
  for(int i = 0; i < nodes.size(); i++) {
    int x = nodes[i];
    maxcost[u][x] = maxcost[x][u] = max(maxcost[x][fa], facost);
  }
  nodes.push_back(u);
  for(int i = 0; i < G[u].size(); i++) {
    int v = G[u][i];
    if(v != fa) dfs(v, u, C[u][i]);
  }
}

double MST() {
  sort(e, e+m);
  for(int i = 0; i < n; i++) { pa[i] = i; G[i].clear(); C[i].clear(); }
  int cnt = 0;
  double ans = 0;
  for(int i = 0; i < m; i++) {
    int x = e[i].x, y = e[i].y, u = findset(x), v = findset(y);
    double d = e[i].d;
    if(u != v) {
      pa[u] = v;
      G[x].push_back(y); C[x].push_back(d);
      G[y].push_back(x); C[y].push_back(d);
      ans += d;
      if(++cnt == n-1) break;
    }
  }
  return ans;
}

//prim求次小生成树
//use[u][v] = 2时,边<u, v>在MST上
//use[u][v] = 1时,原图存在边<u, v>。
//f[u][v]表示u->v的最小瓶颈路 初始化为0
double prim()
{
    int pre[maxn] = {-1};
    bool vis[maxn] = {0};
    double d[maxn], ret = 0;
    FF(i, 1, n+1) d[i] = INF;   d[1] = 0;
    FF(i, 1, n+1)
    {
        int pos;
        double tmp = INF;
        FF(j, 1, n+1) if(!vis[j] && d[j] < tmp) tmp = d[j], pos = j;
        if(pre[pos] != -1)
        {
            use[pre[pos]][pos] = use[pos][pre[pos]] = 2;
            FF(j, 1, n+1) if(vis[j]) f[pos][j] = f[j][pos] = max(f[j][pre[pos]], g[pre[pos]][pos]);
        }
        vis[pos] = 1;
        ret += d[pos];
        FF(j, 1, n+1) if(!vis[j] && use[pos][j] && g[pos][j] < d[j]) d[j] = g[pos][j], pre[j] = pos;
    }
    return ret;
}

//LCA
struct LCA {
  int n;
  int fa[maxn];   // 父亲数组
  int cost[maxn]; // 和父亲的费用
  int L[maxn];    // 层次(根节点层次为0)
  int anc[maxn][logmaxn];     // anc[p][i]是结点p的第2^i级父亲。anc[i][0] = fa[i]
  int maxcost[maxn][logmaxn]; // maxcost[p][i]是i和anc[p][i]的路径上的最大费用

  // 预处理,根据fa和cost数组求出anc和maxcost数组
  void preprocess() {
    for(int i = 0; i < n; i++) {
      anc[i][0] = fa[i]; maxcost[i][0] = cost[i];
      for(int j = 1; (1 << j) < n; j++) anc[i][j] = -1;
    }
    for(int j = 1; (1 << j) < n; j++)
      for(int i = 0; i < n; i++)
        if(anc[i][j-1] != -1) {
          int a = anc[i][j-1];
          anc[i][j] = anc[a][j-1];
          maxcost[i][j] = max(maxcost[i][j-1], maxcost[a][j-1]);
        }
  }

  // 求p到q的路径上的最大权
  int query(int p, int q) {
    int tmp, log, i;
    if(L[p] < L[q]) swap(p, q);
    for(log = 1; (1 << log) <= L[p]; log++); log--;

    int ans = -INF;
    for(int i = log; i >= 0; i--)
      if (L[p] - (1 << i) >= L[q]) { ans = max(ans, maxcost[p][i]); p = anc[p][i];}

    if (p == q) return ans; // LCA为p

    for(int i = log; i >= 0; i--)
      if(anc[p][i] != -1 && anc[p][i] != anc[q][i]) {
        ans = max(ans, maxcost[p][i]); p = anc[p][i];
        ans = max(ans, maxcost[q][i]); q = anc[q][i];
      }

    ans = max(ans, cost[p]);
    ans = max(ans, cost[q]);
    return ans; // LCA为fa[p](它也等于fa[q])
  }
};

//生成树计数问题
int degree[N];
LL C[N][N];
LL det(LL a[][N], int n)//生成树计数:Matrix-Tree定理
{
    LL ret=1;
    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<n; j++)
            while(a[j][i])
            {
                LL t=a[i][i]/a[j][i];
                for(int k=i; k<n; k++)
                    a[i][k]=(a[i][k]-a[j][k]*t);
                for(int k=i; k<n; k++)
                    swap(a[i][k],a[j][k]);
                ret=-ret;
            }
        if(a[i][i]==0)
            return 0;
        ret=ret*a[i][i];
    }
    if(ret<0)
        ret=-ret;
    return ret;
}
void solve()
{
    memset(degree,0,sizeof(degree));
    memset(C,0,sizeof(C));
    scanf("%d%d",&n,&m);
    while(m--)
    {
        scanf("%d%d",&u,&v);
        u--;
        v--;
        C[u][v]=C[v][u]=-1;
        degree[u]++;
        degree[v]++;
    }
    for(int i=0; i<n; ++i)
        C[i][i]=degree[i];
    printf("%lld\n",det(C,n));
}

//离线MST
//n个点m条边,然后给出q个询问,表示把目前第numi条边的边权修改为di后的mst
int n,m,q;
int x[MAXM],y[MAXM],num[MAXM],d[MAXM],f[MAXN],ord[MAXM],t[MAXN];
long long z[MAXM],c[MAXM], answer;

struct Edge{
             int cnt;
             int a[MAXM*5],b[MAXN*5];
             inline void renew(int);
             inline void merge(int u,int v);
             inline int find(int v);
}edge[20];
inline void Edge::renew(int top = 0)
{
       for (;cnt != top; --cnt)
           f[a[cnt]] = b[cnt];
}
inline void Edge::merge(int v,int u)
{
       int _v = find(v);
       int _u = find(u);
       a[++cnt] = _v;
       b[cnt] = f[_v];
       f[_v] = _u;
}
inline int Edge::find(int v)
{
       if (!f[v]) return v;
       int ret = v;
       while (f[ret]) ret = f[ret];
       while (f[v] != ret)
       {
             a[++cnt] = v;
             b[cnt] = f[v];
             f[v] = ret; v = b[cnt];
       }
       return ret;
}
void Qsort(int l,int r)
{
      long long k = z[ord[(l+r)/2]];
      int i = l, j = r;
      while (i<j)
      {
            while (z[ord[i]] < k) i++;
            while (z[ord[j]] > k) j--;
            if (i<=j)
               swap(ord[i++], ord[j--]);
      }
      if (i<r) Qsort(i,r);
      if (l<j) Qsort(l,j);
}
inline void  work(int l ,int r,int dep)
{
      Edge &e = edge[dep];
      e.cnt = 0;
      if (l>r) return ;
      if (l == r)
      {
            z[num[l]] = c[num[l]] = d[l];
            Qsort(1,m);
            long long ans =  answer;
            for (int i = 1; i <= m;++i)
            if (e.find(x[ord[i]]) != e.find(y[ord[i]]))
            {
                 ans+=z[ord[i]];
                 e.merge(x[ord[i]],y[ord[i]]);
            }
            e.renew();
            printf("%lld\n",ans);
            return ;
      }
      int temp_m = m;
      long long  temp_ans = answer;
      //Contrresume;
      for (int i = l; i <= r; ++i) z[num[i]] = -INF;
      Qsort(1,m);
      t[0] = 0;
      for (int i = 1; i <= m; ++i)
      {
          if (e.find(x[ord[i]]) != e.find(y[ord[i]]))
          {
              if (z[ord[i]] != -INF) t[++t[0]] = ord[i];
              e.merge(x[ord[i]],y[ord[i]]);
          }
      }
      e.renew();
      for (int i = 1; i <= t[0] ; ++i)
      {
          e.merge(x[t[i]], y[t[i]]);
          answer +=z[t[i]];
      }
      int temp_cnt = e.cnt;
      //Reduction
      for (int i = l; i <= r; ++i) z[num[i]] = INF;
      Qsort(1,m);
      t[0] =0;
      for (int i = 1; i <= m; ++i)
      {
         if (e.find(x[ord[i]]) != e.find(y[ord[i]]))
         e.merge(x[ord[i]], y[ord[i]]);
         else
         {
             if (z[ord[i]] != INF) t[++t[0]] = i;
         }
      }
      for (int i = t[0]; i ; --i) swap(ord[t[i]],ord[m--]);
      e.renew(temp_cnt);
      for (int i =l ; i<=r; ++i)  z[num[i]] = c[num[i]];
      work(l,(l+r)/2,  dep+1);
      work((l+r)/2+1,r,dep+1);
      e.renew();
      answer = temp_ans;
      m = temp_m;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m ;++i)
    {
        scanf("%d %d %d", &x[i],&y[i],&z[i]);
        c[i] = z[i];
        ord[i] = i;
    }
    scanf("%d",&q);
    Qsort(1,m);
    for (int i = 1; i <= q; ++i)
        scanf("%d %d", &num[i], &d[i]);
    work(1,q,0);
    return 0;
}

//固定根的最小树型图,邻接矩阵写法
struct MDST {
  int n;
  int w[maxn][maxn]; // 边权
  int vis[maxn];     // 访问标记,仅用来判断无解
  int ans;           // 计算答案
  int removed[maxn]; // 每个点是否被删除
  int cid[maxn];     // 所在圈编号
  int pre[maxn];     // 最小入边的起点
  int iw[maxn];      // 最小入边的权值
  int max_cid;       // 最大圈编号

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++)
      for(int j = 0; j < n; j++) w[i][j] = INF;
  }

  void AddEdge(int u, int v, int cost) {
    w[u][v] = min(w[u][v], cost); // 重边取权最小的
  }

  // 从s出发能到达多少个结点
  int dfs(int s) {
    vis[s] = 1;
    int ans = 1;
    for(int i = 0; i < n; i++)
      if(!vis[i] && w[s][i] < INF) ans += dfs(i);
    return ans;
  }

  // 从u出发沿着pre指针找圈
  bool cycle(int u) {
    max_cid++;
    int v = u;
    while(cid[v] != max_cid) { cid[v] = max_cid; v = pre[v]; }
    return v == u;
  }

  // 计算u的最小入弧,入弧起点不得在圈c中
  void update(int u) {
    iw[u] = INF;
    for(int i = 0; i < n; i++)
      if(!removed[i] && w[i][u] < iw[u]) {
        iw[u] = w[i][u];
        pre[u] = i;
      }
  }

  // 根结点为s,如果失败则返回false
  bool solve(int s) {
    memset(vis, 0, sizeof(vis));
    if(dfs(s) != n) return false;

    memset(removed, 0, sizeof(removed));
    memset(cid, 0, sizeof(cid));
    for(int u = 0; u < n; u++) update(u);
    pre[s] = s; iw[s] = 0; // 根结点特殊处理
    ans = max_cid = 0;
    for(;;) {
      bool have_cycle = false;
      for(int u = 0; u < n; u++) if(u != s && !removed[u] && cycle(u)){
        have_cycle = true;
        // 以下代码缩圈,圈上除了u之外的结点均删除
        int v = u;
        do {
          if(v != u) removed[v] = 1;
          ans += iw[v];
          // 对于圈外点i,把边i->v改成i->u(并调整权值);v->i改为u->i
          // 注意圈上可能还有一个v'使得i->v'或者v'->i存在,因此只保留权值最小的i->u和u->i
          for(int i = 0; i < n; i++) if(cid[i] != cid[u] && !removed[i]) {
            if(w[i][v] < INF) w[i][u] = min(w[i][u], w[i][v]-iw[v]);
            w[u][i] = min(w[u][i], w[v][i]);
            if(pre[i] == v) pre[i] = u;
          }
          v = pre[v];
        } while(v != u);
        update(u);
        break;
      }
      if(!have_cycle) break;
    }
    for(int i = 0; i < n; i++)
      if(!removed[i]) ans += iw[i];
    return true;
  }
};


//KM    time : O(n^4)
int W[maxn][maxn], n;
int Lx[maxn], Ly[maxn]; // 顶标
int left[maxn];         // left[i]为右边第i个点的匹配点编号
bool S[maxn], T[maxn];   // S[i]和T[i]为左/右第i个点是否已标记

bool match(int i){
  S[i] = true;
  for(int j = 1; j <= n; j++) if (Lx[i]+Ly[j] == W[i][j] && !T[j]){
    T[j] = true;
    if (!left[j] || match(left[j])){
      left[j] = i;
      return true;
    }
  }
  return false;
}

void update(){
  int a = INF;
  for(int i = 1; i <= n; i++) if(S[i])
    for(int j = 1; j <= n; j++) if(!T[j])
      a = min(a, Lx[i]+Ly[j] - W[i][j]);
  for(int i = 1; i <= n; i++) {
    if(S[i]) Lx[i] -= a;
    if(T[i]) Ly[i] += a;
  }
}

void KM() {
  for(int i = 1; i <= n; i++) {
    left[i] = Lx[i] = Ly[i] = 0;
    for(int j = 1; j <= n; j++)
      Lx[i] = max(Lx[i], W[i][j]);
  }
  for(int i = 1; i <= n; i++) {
    for(;;) {
      for(int j = 1; j <= n; j++) S[j] = T[j] = false;
      if(match(i)) break; else update();
    }
  }
}

// KM   time O(n^3)

int n;
int match[maxn], t[maxn], s[maxn];
double w[maxn][maxn], lx[maxn], ly[maxn], slack[maxn];
bool path(int u) {
    int v;
    s[u] = true;
    for(v = 1; v <= n; v++)
        if(!t[v]) {
            double tmp = lx[u] + ly[v] - w[u][v];
            if(fabs(tmp) < 1e-6) {
                t[v] = true;
                if(match[v] == -1 || path(match[v])) {
                    match[v] = u;
                    return true;
                }
            }
            else slack[v] = min(slack[v], tmp);
        }
    return false;
}

void Update() {
    double d = inf;
    int i;
    for(i = 1; i <= n; i++)
        if(!t[i]) d = min(d, slack[i]);
    for(i = 1; i <= n; i++)
        if(s[i]) lx[i] -= d;
    for(i = 1; i <= n; i++)
        if(t[i]) ly[i] += d;
        else slack[i] -= d;
}

void KM() {
    int i, j;
    for(i = 1; i <= n; i++) {
        match[i] = -1;
        ly[i] = 0;
        lx[i] = -inf;
        for(j = 1; j <= n; j++)
            lx[i] = max(lx[i], w[i][j]);
    }
    for(i = 1; i <= n; i++) {
        for(j = 1; j <= n; j++)
            slack[j] = inf;
        while(true) {
            memset(s, false, sizeof(s));
            memset(t, false, sizeof(t));
            if(path(i)) break;
            else Update();
        }
    }
}

 // 二分图最大基数匹配,邻接矩阵写法
struct BPM
{
  int n, m;               // 左右顶点个数
  int G[maxn][maxn];      // 邻接表
  int left[maxn];         // left[i]为右边第i个点的匹配点编号,-1表示不存在
  bool T[maxn];           // T[i]为右边第i个点是否已标记

  void init(int n, int m) {
    this->n = n;
    this->m = m;
    memset(G, 0, sizeof(G));
  }

  bool match(int u){
    for(int v = 0; v < m; v++) if(G[u][v] && !T[v]) {
      T[v] = true;
      if (left[v] == -1 || match(left[v])){
        left[v] = u;
        return true;
      }
    }
    return false;
  }
  // 求最大匹配
  int solve() {
    memset(left, -1, sizeof(left));
    int ans = 0;
    for(int u = 0; u < n; u++) { // 从左边结点u开始增广
      memset(T, 0, sizeof(T));
      if(match(u)) ans++;
    }
    return ans;
  }
};

// 二分图最大基数匹配求覆盖集
struct BPM {
  int n, m;               // 左右顶点个数
  vector<int> G[maxn];    // 邻接表
  int left[maxn];         // left[i]为右边第i个点的匹配点编号,-1表示不存在
  bool T[maxn];           // T[i]为右边第i个点是否已标记

  int right[maxn];        // 求最小覆盖用
  bool S[maxn];           // 求最小覆盖用

  void init(int n, int m) {
    this->n = n;
    this->m = m;
    for(int i = 0; i < n; i++) G[i].clear();
  }

  void AddEdge(int u, int v) {
    G[u].push_back(v);
  }

  bool match(int u){
    S[u] = true;
    for(int i = 0; i < G[u].size(); i++) {
      int v = G[u][i];
      if (!T[v]){
        T[v] = true;
        if (left[v] == -1 || match(left[v])){
          left[v] = u;
          right[u] = v;
          return true;
        }
      }
    }
    return false;
  }

  // 求最大匹配
  int solve() {
    memset(left, -1, sizeof(left));
    memset(right, -1, sizeof(right));
    int ans = 0;
    for(int u = 0; u < n; u++) { // 从左边结点u开始增广
      memset(S, 0, sizeof(S));
      memset(T, 0, sizeof(T));
      if(match(u)) ans++;
    }
    return ans;
  }

  // 求最小覆盖。X和Y为最小覆盖中的点集
  int mincover(vector<int>& X, vector<int>& Y) {
    int ans = solve();
    memset(S, 0, sizeof(S));
    memset(T, 0, sizeof(T));
    for(int u = 0; u < n; u++)
      if(right[u] == -1) match(u); // 从所有X未盖点出发增广
    for(int u = 0; u < n; u++)
      if(!S[u]) X.push_back(u); // X中的未标记点
    for(int v = 0; v < m; v++)
      if(T[v]) Y.push_back(v);  // Y中的已标记点
   return ans;
  }
};
//二分匹配 邻接表版本
struct Edge
{
	int to,next;
}edge[MAXM];
int head[MAXN],tot;
void init()
{
	tot = 0;
	CLR(head, -1);
}
void addEdge(int u,int v)
{
	edge[tot].to = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}
int linker[MAXN];
int used[MAXN];
//时间戳when优化
int uN, when;

bool dfs(int u)
{
	for(int i = head[u];i != -1;i = edge[i].next)
	{
		int v = edge[i].to;
		if(used[v] != when)
		{
			used[v] = when;
			if(linker[v] == -1 || dfs(linker[v]))
			{
				linker[v] = u;
				return true;
			}
		}
	}
	return false;
}
bool hungary()
{
	memset(linker,-1,sizeof(linker));
	when = 0;
	for(int u = 0; u < uN;u++)
	{
		when++;
		if(!dfs(u))return false;
	}
	return true;
}

//ISAP
struct Edge {
  int from, to, cap, flow;
};
bool operator < (const Edge& a, const Edge& b) {
  return a.from < b.from || (a.from == b.from && a.to < b.to);
}

struct ISAP {
  int n, m, s, t;
  vector<Edge> edges;
  vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
  bool vis[maxn];        // BFS使用
  int d[maxn];           // 从起点到i的距离
  int cur[maxn];        // 当前弧指针
  int p[maxn];          // 可增广路上的上一条弧
  int num[maxn];        // 距离标号计数

  void AddEdge(int from, int to, int cap) {
    edges.push_back((Edge){from, to, cap, 0});
    edges.push_back((Edge){to, from, 0, 0});
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
  }

  bool BFS() {
    memset(vis, 0, sizeof(vis));
    queue<int> Q;
    Q.push(t);
    vis[t] = 1;
    d[t] = 0;
    while(!Q.empty()) {
      int x = Q.front(); Q.pop();
      for(int i = 0; i < G[x].size(); i++) {
        Edge& e = edges[G[x][i]^1];
        if(!vis[e.from] && e.cap > e.flow) {
          vis[e.from] = 1;
          d[e.from] = d[x] + 1;
          Q.push(e.from);
        }
      }
    }
    return vis[s];
  }

  void ClearAll(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void ClearFlow() {
    for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
  }

  int Augment() {
    int x = t, a = INF;
    while(x != s) {
      Edge& e = edges[p[x]];
      a = min(a, e.cap-e.flow);
      x = edges[p[x]].from;
    }
    x = t;
    while(x != s) {
      edges[p[x]].flow += a;
      edges[p[x]^1].flow -= a;
      x = edges[p[x]].from;
    }
    return a;
  }

  int Maxflow(int s, int t, int need) {
    this->s = s; this->t = t;
    int flow = 0;
    BFS();
    memset(num, 0, sizeof(num));
    for(int i = 0; i < n; i++) num[d[i]]++;
    int x = s;
    memset(cur, 0, sizeof(cur));
    while(d[s] < n) {
      if(x == t) {
        flow += Augment();
        if(flow >= need) return flow;
        x = s;
      }
      int ok = 0;
      for(int i = cur[x]; i < G[x].size(); i++) {
        Edge& e = edges[G[x][i]];
        if(e.cap > e.flow && d[x] == d[e.to] + 1) { // Advance
          ok = 1;
          p[e.to] = G[x][i];
          cur[x] = i; // 注意
          x = e.to;
          break;
        }
      }
      if(!ok) { // Retreat
        int m = n-1; // 初值注意
        for(int i = 0; i < G[x].size(); i++) {
          Edge& e = edges[G[x][i]];
          if(e.cap > e.flow) m = min(m, d[e.to]);
        }
        if(--num[d[x]] == 0) break;
        num[d[x] = m+1]++;
        cur[x] = 0; // 注意
        if(x != s) x = edges[p[x]].from;
      }
    }
    return flow;
  }

  vector<int> Mincut() { // call this after maxflow
    BFS();
    vector<int> ans;
    for(int i = 0; i < edges.size(); i++) {
      Edge& e = edges[i];
      if(!vis[e.from] && vis[e.to] && e.cap > 0) ans.push_back(i);
    }
    return ans;
  }

  void Reduce() {
    for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
  }

  void print() {
    printf("Graph:\n");
    for(int i = 0; i < edges.size(); i++)
      printf("%d->%d, %d, %d\n", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow);
  }
};

//Dinic
struct Edge {
  int from, to, cap, flow;
};
bool operator < (const Edge& a, const Edge& b) {
  return a.from < b.from || (a.from == b.from && a.to < b.to);
}

struct Dinic {
  int n, m, s, t;
  vector<Edge> edges;    // 边数的两倍
  vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
  bool vis[maxn];        // BFS使用
  int d[maxn];           // 从起点到i的距离
  int cur[maxn];         // 当前弧指针

  void ClearAll(int n) {
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void ClearFlow() {
    for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
  }

  void AddEdge(int from, int to, int cap) {
    edges.push_back((Edge){from, to, cap, 0});
    edges.push_back((Edge){to, from, 0, 0});
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
  }

  bool BFS() {
    memset(vis, 0, sizeof(vis));
    queue<int> Q;
    Q.push(s);
    vis[s] = 1;
    d[s] = 0;
    while(!Q.empty()) {
      int x = Q.front(); Q.pop();
      for(int i = 0; i < G[x].size(); i++) {
        Edge& e = edges[G[x][i]];
        if(!vis[e.to] && e.cap > e.flow) {
          vis[e.to] = 1;
          d[e.to] = d[x] + 1;
          Q.push(e.to);
        }
      }
    }
    return vis[t];
  }

  int DFS(int x, int a) {
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[x]; i < G[x].size(); i++) {
      Edge& e = edges[G[x][i]];
      if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
        e.flow += f;
        edges[G[x][i]^1].flow -= f;
        flow += f;
        a -= f;
        if(a == 0) break;
      }
    }
    return flow;
  }

  int Maxflow(int s, int t) {
    this->s = s; this->t = t;
    int flow = 0;
    while(BFS()) {
      memset(cur, 0, sizeof(cur));
      flow += DFS(s, INF);
    }
    return flow;
  }
};

//MCMF
struct Edge {
  int from, to, cap, flow, cost;
  Edge(){}
  Edge(int a, int b, int c, int d, int e):from(a),  to(b), cap(c), flow(d), cost(e){}
};
struct MCMF {
  int n, m, s, t;
  vector<Edge> edges;
  vector<int> G[maxn];
  int inq[maxn];         // 是否在队列中
  int d[maxn];           // Bellman-Ford
  int p[maxn];           // 上一条弧
  int a[maxn];           // 可改进量

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void AddEdge(int from, int to, int cap, int cost) {
    Edge e1 = Edge(from, to, cap, 0, cost), e2 = Edge(to, from, 0, 0, -cost);
    edges.push_back(e1);
    edges.push_back(e2);
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
  }

  bool spfa(int s, int t, int &flow, int &cost)  {
    for(int i = 0; i < n; i ++) d[i] = INF;
    memset(inq, 0, sizeof(inq));
    d[s] = 0;   inq[s] = 1; p[s] = 0;   a[s] = INF;
    queue<int> Q; Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        REP(i, G[u].size())
        {
            Edge& e = edges[G[u][i]];
            if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
            {
                d[e.to] = d[u] + e.cost;
                p[e.to] = G[u][i];
                a[e.to] = min(a[u], e.cap - e.flow);
                if(!inq[e.to])
                {
                    Q.push(e.to);
                    inq[e.to] = 1;
                }
            }
        }
    }
    if(d[t] == INF)    return false;//!!!
    flow += a[t];
    cost += d[t] * a[t];
    int u = t;
    while(u != s)
    {
        edges[p[u]].flow += a[t];
        edges[p[u]^1].flow -= a[t];
        u = edges[p[u]].from;
    }
    return true;
  }

  // 需要保证初始网络中没有负权圈
  int Mincost(int s, int t, int& flow, int& cost) {
    flow = cost = 0;
    while(spfa(s, t, flow, cost));
    return cost;
  }
};

//ZKW费用流
struct ZKW_flow{
    int st, ed, ecnt, n;
    int head[MAXN];
    int cap[MAXE], cost[MAXE], to[MAXE], next[MAXE], dis[MAXN]; ;

    void init(){
        memset(head, 0, sizeof(head));
        ecnt = 2;
    }

    void addEdge(int u, int v, int cc, int ww){
        cap[ecnt] = cc; cost[ecnt] = ww; to[ecnt] = v;
        next[ecnt] = head[u]; head[u] = ecnt++;
        cap[ecnt] = 0; cost[ecnt] = -ww; to[ecnt] = u;
        next[ecnt] = head[v]; head[v] = ecnt++;
    }

    void SPFA(){
        for(int i = 1; i <= n; ++i) dis[i] = INF;
        priority_queue<pair<int, int> > Q;
        dis[st] = 0;
        Q.push(make_pair(0, st));
        while(!Q.empty()){
            int u = Q.top().second, d = -Q.top().first;
            Q.pop();
            if(dis[u] != d) continue;
            for(int p = head[u]; p; p = next[p]){
                int &v = to[p];
                if(cap[p] && dis[v] > d + cost[p]){
                    dis[v] = d + cost[p];
                    Q.push(make_pair(-dis[v], v));
                }
            }
        }
        for(int i = 1; i <= n; ++i) dis[i] = dis[ed] - dis[i];
    }

    int minCost, maxFlow;
    bool use[MAXN];

    int add_flow(int u, int flow){
        if(u == ed){
            maxFlow += flow;
            minCost += dis[st] * flow;
            return flow;
        }
        use[u] = true;
        int now = flow;
        for(int p = head[u]; p; p = next[p]){
            int &v = to[p];
            if(cap[p] && !use[v] && dis[u] == dis[v] + cost[p]){
                int tmp = add_flow(v, min(now, cap[p]));
                cap[p] -= tmp;
                cap[p^1] += tmp;
                now -= tmp;
                if(!now) break;
            }
        }
        return flow - now;
    }

    bool modify_label(){
        int d = INF;
        for(int u = 1; u <= n; ++u) if(use[u])
            for(int p = head[u]; p; p = next[p]){
                int &v = to[p];
                if(cap[p] && !use[v]) d = min(d, dis[v] + cost[p] - dis[u]);
            }
        if(d == INF) return false;
        for(int i = 1; i <= n; ++i) if(use[i]) dis[i] += d;
        return true;
    }

    int min_cost_flow(int ss, int tt, int nn){
        st = ss, ed = tt, n = nn;///点是1 - n
        minCost = maxFlow = 0;
        SPFA();
        while(true){
            while(true){
                for(int i = 1; i <= n; ++i) use[i] = 0;
                if(!add_flow(st, INF)) break;
            }
            if(!modify_label()) break;
        }
        return minCost;
    }
};

//斯坦纳树
//此代码用于求hdu4085 最小斯坦纳森林
const int N = (1<<10) + 10;
int n, m, K, tot;
int s[maxn], d[maxn][N], dp[N], u, v, w;
bool vis[maxn][N];

void spfa()
{
    while(!q.empty())
    {
        int x = q.front() / MOD, y = q.front() % MOD;
        q.pop();
        vis[x][y] = 0;
        int nc = G[x].size();
        REP(i, nc)
        {
            Edge e = edges[G[x][i]];
            int v = e.to, t = y | s[v];
            if(d[v][t] > d[x][y] + e.dist)
            {
                d[v][t] = d[x][y] + e.dist;
                if(t == y && !vis[v][y])
                {
                    vis[v][y] = 1;
                    q.push(v*MOD + y);
                }
            }
        }
    }
}

void init()
{
    FF(i, 1, n+1) G[i].clear(); edges.clear();
    CLR(s, 0);
    scanf("%d%d%d", &n, &m, &K);
    tot=1<<(2*K);
    for(int i=1;i<=n;i++)
        for(int j=0;j<tot;j++)
            d[i][j]=INF;
    while(m--)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(int i=1;i<=K;i++)
    {
        s[i]=1<<(i-1),d[i][s[i]]=0;
        s[n-i+1]=1<<(K+i-1),d[n-i+1][s[n-i+1]]=0;
    }
}

void go_spfa()
{
    REP(y, tot)
    {
        FF(x, 1, n+1)
        {
            for(int i=(y-1)&y; i; i=(i-1)&y)
                d[x][y] = min(d[x][y], d[x][i|s[x]] + d[x][(y-i)|s[x]]);
            if(d[x][y] < INF)
            {
                q.push(x*MOD + y);
                vis[x][y] = 1;
            }
        }
        spfa();
    }
}

void solve()
{
    REP(y, tot)
    {
        dp[y] = INF;
        FF(x, 1, n+1) dp[y] = min(dp[y], d[x][y]);
    }

    FF(i, 1, tot) if(check(i))
        for(int j=(i-1)&i; j; j=(j-1)&i) if(check(j))
            dp[i] = min(dp[i], dp[j] + dp[i-j]);

    if(dp[tot-1] >= INF) puts("No solution");
    else printf("%d\n", dp[tot-1]);
}

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        init();
        go_spfa();
        solve();
    }
    return 0;
}

//次小生成树 最佳替代边
//当边<i, j>为MST边时 dp[i][j]为边ij的最佳替代边
void prim()
{
    REP(i, n) d[i] = g[0][i];
    vis[0] = 1;
    fa[0] = -1;
    d[0] = INF;
    mst = 0;
    FF(i, 1, n)
    {
        int pos = 0;
        FF(j, 1, n) if(!vis[j] && d[pos] > d[j]) pos = j;

        mst += d[pos];
        vis[pos] = 1;

        //构造MST
        G[pos].PB(fa[pos]);
        G[fa[pos]].PB(pos);

        FF(j, 1, n) if(!vis[j] && g[pos][j] < d[j])
            d[j] = g[pos][j], fa[j] = pos;
    }
}

//用所有非MST边g[p][i] 更新所有dp[u][v]
double dfs(int p, int u, int f)
{
    double ans = INF;
    REP(i, G[u].size())
    {
        int v = G[u][i];
        if(v != f)
        {
            double tmp = dfs(p, v, u);
            ans = min(ans, tmp);
            dp[u][v] = dp[v][u] = min(dp[u][v], tmp);
        }
    }
    //保证非MST边才能更新
    if(p != f) ans = min(ans, g[p][u]);
    return ans;
}

void solve()
{
    prim();
    REP(i, n) dfs(i, i, -1); //每个点更新一次
}

//全局最小割
int map[MAXN][MAXN];
int v[MAXN], dis[MAXN]; //v数组是马甲数组,dis数组用来表示该点与A集合中所有点之间的边的长度之和
bool vis[MAXN];//用来标记是否该点加入了A集合
int Stoer_Wagner(int n)
{
    int i, j, res = INF;
    for(i = 0; i < n; i ++)
        v[i] = i; //初始马甲为自己
    while(n > 1)
    {
        int k, pre = 0;  //pre用来表示之前加入A集合的点,我们每次都以0点为第一个加入A集合的点
        memset(vis, 0, sizeof(vis));
        memset(dis, 0, sizeof(dis));
        for(i = 1; i < n; i ++)
        {
            k = -1;
            for(j = 1; j < n; j ++)  //根据之前加入的点,要更新dis数组,并找到最大的dis
                if(!vis[v[j]])
                {
                    dis[v[j]] += map[v[pre]][v[j]];
                    if(k == -1 || dis[v[k]] < dis[v[j]])
                        k = j;
                }
            vis[v[k]] = true;//标记该点已经加入A集合
            if(i == n - 1) //最后一次加入的点就要更新答案了
            {
                res = min(res, dis[v[k]]);
                for(j = 0; j < n; j ++) //将该点合并到pre上,相应的边权就要合并
                {
                    map[v[pre]][v[j]] += map[v[j]][v[k]];
                    map[v[j]][v[pre]] += map[v[j]][v[k]];
                }
                v[k] = v[-- n];//删除最后一个点
            }
            pre = k;
        }
    }
    return res;
}

/* 
无源汇上下界网络流:构图如下: 
1、首先对于与每条边(u,v,L,H),u->v连一条容量为H-L的边 
2、创建一个源S,和汇T 
3、对于任意一个结点,如果u出边下界和 OutL > 入边下界和InL,则u->T一条OutL - InL的边。 
否则连S->u一条InL-OutL的边。 
4、求s-t最大流,若与S关联的边满容量,则有解。则残余网络中u->v的流量+其原来图的下界构成一个可行流 
*/  
  
/* 
有源汇(S T)上下界最小流 
像无源无汇上下界可行流那样先建好图,记图的超级源点为SS,超级汇点为TT。 
先从SS到TT跑一遍最大流,然后加边T->S容量为无穷大,然后再从SS到TT跑一遍最大流,若与SS关联的边满容量,则有解。 
其中最小流为最后加进去的n→1的边的流量,找边的流量跟无源无汇上下界可行流一样,否则无解。 
*/  
  
/* 
有源汇(S T)上下界最大流 
像无源无汇上下界可行流那样先建好图,记图的超级源点为SS,超级汇点为TT。
然后T到S连一条边,容量为无穷大。
从SS->TT跑一遍最大流 判可行性 
最后从源点S到汇点T跑一遍最大流就是答案, 
每条边容量的取法和无源无汇上下界可行流一样。 
*/ 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值