转自: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跑一遍最大流就是答案,
每条边容量的取法和无源无汇上下界可行流一样。
*/