图论模板

来源:互联网 发布:中国mba排名 知乎 编辑:程序博客网 时间:2024/06/05 18:34

转自: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;  }};//堆优化的Dijkstrastruct 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] = 0int 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的最小瓶颈路 初始化为0double 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;}//LCAstruct 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后的mstint 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;}//ISAPstruct 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);  }};//Dinicstruct 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;  }};//MCMFstruct 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