模板大部分来自LRJ
//带重边处理
int tarjan(int u, int fa)
{
bool f = false;
int lowu = dfn[u] = ++dfs_c;
REP(i, G[u].size())
{
Edge& e = edges[G[u][i]];
if (e.to == fa && !f) ///////第一次为访问父节点不算, 其他则为重边
{
f = 1;
continue;
}
if (!dfn[e.to])
{
int lowv = tarjan(e.to, u);
lowu = min(lowu, lowv);
if (lowv > dfn[u])
{
edges[G[u][i]].flag = true;
edges[G[u][i] ^ 1].flag = true;
}
}
else
lowu = min(lowu, dfn[e.to]);
}
low[u] = lowu;
return lowu;
}
vector G[MAXN];
int pre[MAXN], low[MAXN], iscut[MAXN];
///pre[u]表示dfs中u的次序编号
///low[u]表示u及u的子树中能追溯到的最早的节点的次序编号值(即pre值)
///iscut[u]表示u是否为割点
int dfs_clock = 0;
///调用前需将pre数组赋0,首次调用fa为-1
int dfs(int u, int fa)///fa表示u的父节点
{
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]) ///若没被访问
{
int lowv = dfs(v, u);
lowu = min(lowv, lowu); ///用子节点的low值更新lowu
if (lowv >= pre[u] && fa >= 0)
{
iscut[u] = 1;
}
}///若被访问,则用反向变(返祖边)更新当前u的low值
else if (v != fa && pre[v] < pre[u])///切记v != fa
{
lowu = min(lowu, pre[v]);
}
}
if (fa < 0 && child > 1) ///根节点为割点当且仅当子节点数大于一个
iscut[u] = 1;
low[u] = lowu;
return lowu;
}
求删割点后剩余联通分量数时,只用将iscut[u] = 1改为iscut[u]++,则非根节点剩余分量数为iscut[u]+1,根节点为iscut[u]
(若u为割点,记iscut[u]为u的子节点数,则去掉u后,图被分成iscut[u]+1个部分(每个子节点的部分和u的祖先的部分),若u为dfs树的根,则分成iscut[u]个部分(根节点没有祖先))
//当u为割点时,把边从栈顶依次取出,直到遇到边(u,v),取出的边与其关联的点,组成点双连通分支。
//割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支
//基本算法与求割点一致
int dfn[MAXN], iscut[MAXN], bccno[MAXN];
int dfs_clock, bcc_cnt;
vector G[MAXN], bcc[MAXN];
struct Edge{
int u, v;
};
stack S;
int dfs(int u, int fa)
{
int lowu = dfn[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 (!dfn[v])
{
S.push(e);
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if (lowv >= dfn[u])
{
iscut[u] = 1;
bcc_cnt++;
bcc[bcc_cnt].clear();
while (1)
{
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)///最后才判断跳出,因为(u,v)这条变也要加入
break;
}
}
}
else if (v != fa && dfn[v] < dfn[u])
{
S.push(e);
lowu = min(lowu, dfn[v]);
}
}
if (fa < 0 && child == 1)
iscut[u] = 0;
return lowu;
}
void find_bcc(int n)
{
memset(dfn, 0, sizeof(dfn));
memset(iscut, 0, sizeof(iscut));
memset(bccno, 0, sizeof(bccno));
dfs_clock = bcc_cnt = 0;
for (int i = 1; i <= n; i++)
if (!dfn[i])
dfs(i, -1);
}
struct Edge{
int from, to;
bool flag;
};
vector edges;
VI G[maxn];
int dfn[maxn], bccno[maxn], out[maxn];
int dfs_c, bcc_cnt, n, m;
bool vis[maxn];
inline void add(int x, int y)
{
edges.PB((Edge){x, y, false});
edges.PB((Edge){y, x, false});
int sz = edges.size();
G[x].PB(sz - 2);
G[y].PB(sz - 1);
}
int tarjan(int u, int fa)
{
int lowu = dfn[u] = ++dfs_c;
REP(i, SZ(G[u]))
{
Edge& e = edges[G[u][i]];
if (!dfn[e.to])
{
int lowv = tarjan(e.to, u);
lowu = min(lowu, lowv);
if (lowv > dfn[u])
{
e.flag = true;
edges[G[u][i] ^ 1].flag = 1;
}
}
else if (dfn[e.to] < dfn[u] && e.to != fa)
lowu = min(lowu, dfn[e.to]);
}
return lowu;
}
void find_bcc()
{
CLR(dfn, 0), CLR(bccno, 0);
CLR(out, 0);
dfs_c = bcc_cnt = 0;
FE(i, 1, n)
if (!dfn[i])
tarjan(i, -1);
}
void init()
{
FE(i, 0, n + 1)
G[i].clear();
edges.clear();
CLR(vis, 0);
}
void dfs(int u, int cnt)
{
vis[u] = 1, bccno[u] = cnt;
REP(i, SZ(G[u]))
{
Edge& e = edges[G[u][i]];
if (e.flag)
{
//printf("Bridge: %d %d\n", e.from, e.to);
continue;
}
if (!vis[e.to])
dfs(e.to, cnt);
}
}
void solve()
{
FE(i, 1, n)
if (!vis[i])
{
bcc_cnt++;
dfs(i, bcc_cnt);
}
REP(i, SZ(edges))
{
int u = edges[i].from, v = edges[i].to;
if (bccno[u] != bccno[v])
out[bccno[u]]++;
}
int leaf = 0;
FE(i, 1, bcc_cnt)
if (out[i] == 1)
leaf++;
//cout << bcc_cnt << " " << leaf << endl;
int ans = (leaf + 1) / 2;
if (bcc_cnt == 1) ans = 0;
WI(ans);
}
int main()
{
int x, y;
while (~RII(n, m))
{
init();
REP(i, m)
{
RII(x, y);
add(x, y);
}
find_bcc();
solve();
}
}
重边处理
//对于有重边的图,求边双连通分量
struct EDGE
{
int u, v;
int next;
};
int first[MAXN], rear;
EDGE edge[MAXE];
void init()
{
memset(first, -1, sizeof(first));
rear = 0;
}
void insert(int tu, int tv)
{
edge[rear].u = tu;
edge[rear].v = tv;
edge[rear].next = first[tu];
first[tu] = rear++;
edge[rear].u = tv;
edge[rear].v = tu;
edge[rear].next = first[tv];
first[tv] = rear++;
}
int pre[MAXN], low[MAXN];
bool vis_e[MAXE];
bool is_cut_e[MAXE];
int dfs_clock;
int dfs(int cur, int fa)
{
pre[cur] = low[cur] = ++dfs_clock;
int child(0);
for(int i = first[cur]; i != -1; i = edge[i].next)
{
int tv = edge[i].v;
if(!pre[tv])
{
++child;
vis_e[i] = true;
vie_e[i^1] = true;
st.push(i);
int lowv = dfs(tv, cur);
low[cur] = min(low[cur], lowv);
}
else
if(pre[tv] < pre[cur] && !vis_e[i])
{
vis_e[i] = true;
vis_e[i^1] = true;
low[cur] = min(low[cur], pre[edge[i].v]);
}
}
return low[cur];
}
void find_e_bccn(int n)
{
dfs_clock = 0;
memset(pre, 0, sizeof(pre));
memset(low, 0, sizeof(low));
memset(vis_e, 0, sizeof(vis_e));
memset(is_cut_e, 0, sizeof(is_cut_e));
for(int i = 1; i <= n; ++i)
if(!pre[i])
dfs(i);
for(int i = 0; i < rear; ++i)
if(low[i].v < pre[edge[i].u])
{
is_cut_e[i] = true;
is_cut_e[i^1] = true;
}
}
//接着在不经过桥的情况下dfs求出所有双强连通分量即可
vector G[MAXN];
int dfn[MAXN], low[MAXN], sccno[MAXN], dfs_clock, scc_cnt;
stack S;
void dfs(int u)
{
dfn[u] = low[u] = ++dfs_clock;
S.push(u);
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if (!dfn[u])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (!sccno[v]) ///只能通过当前SCC中的点更新
low[u] = min(low[u], low[v]);
}
if (low[u] == dfn[u]) ///当u是此连通分量中最先被遍历的点
{
scc_cnt++;
while (1)
{
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(dfn, 0, sizeof(dfn));
for (int i = 0; i < n; i++)
if (!dfn[i])
dfs(i);
}
//求至少需要选择多少个点才能是全图都能被访问到
//至少加多少条边才能使全图连通
//解:缩点,行成一个DAG图,则A为DAG中入度为0的点(块)的个数
// B为入度或出度为0的点的个数的最大值
struct Edge{
int from, to, dist;
};
struct Node{
int d, u;
bool operator < (const Node& rhs) const
{
return d > rhs.d;
}
};
struct Dijkstra{
int n, m;
vector edges;
vector G[MAXN];
bool done[MAXN];
int d[MAXN];
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 Q;
for (int i = 0; i < n; i++)
d[i] = INF;
d[s] = 0;
memset(done, 0, sizeof(done));
Q.push((Node){0, s});
while (!Q.empty())
{
Node x = Q.top();
Q.pop();
int u = x.u;
if (done[u])
continue;
done[u] = 1;
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((Node){d[e.to], e.to});
}
}
}
}
};
struct Edge{
int from, to, dist;
};
struct BellmanFord{
int n, m;
vector edges;
vector G[MAXN];
bool inq[MAXN];
int d[MAXN];
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, int dist)
{
edges.push_back((Edge){from, to, dist});
m = edges.size();
G[from].push_back(m - 1);
}
bool negativeCycle()
{
queue Q;
memset(inq, 0, sizeof(inq));
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; i++)///若使用spfa算法,则修改为源点入队,d[]数组赋值INF,d[s]=0
{
d[i] = 0;
inq[i] = true;
Q.push(i);
}
while (!Q.empty())
{
int u = Q.front();
Q.pop();
inq[u] = 0;
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] = 1;
if (++cnt[e.to] > n)
return true;
}
}
}
}
return false;
}
};
//DFS版本判负环
struct BellmanFord{
int n, m;
vector edges;
VI G[maxn];
bool inq[maxn];
double d[maxn];
int cnt[maxn];
void init(int n)
{
this-> n = n;
REP(i, n + 1)
G[i].clear();
edges.clear();
}
void add(int from, int to, double dist)
{
Edge e1 = Edge(from, to, dist);
edges.PB(e1);
m = edges.size();
G[from].PB(m - 1);
}
bool dfs(int u)
{
inq[u] = 1;
REP(i, SZ(G[u]))
{
Edge &e = edges[G[u][i]];
if (d[e.to] > d[u] * e.dist)
{
d[e.to] = d[u] * e.dist;
if (inq[e.to] || dfs(e.to)) return 1;
}
}
inq[u] = 0;
return 0;
}
bool spfa(int s)
{
queue Q;
CLR(inq, false), CLR(cnt, 0);
REP(i, n)
d[i] = INF;
d[s] = 1.0, inq[s] = 1;
Q.push(s);
REP(i, n)
if (dfs(i))
return 1;
return 0;
}
}neg;
const int INF = 0x3f3f3f3f;
const int MAX = 1005;
int n,m;
int start,end,k;
struct Edge
{
int w;
int to;
int next;
};
Edge e[100005];
int head[MAX],edgeNum;
int dis[MAX]; //dis[i]表示从i点到end的最短距离
bool vis[MAX];
int cnt[MAX];
vector opp_Graph[MAX];
struct Node
{
int f,g; //f = g+dis[v]
int v; //当前到达的节点
Node(int a, int b,int c):f(a),g(b),v(c){}
bool operator < (const Node& a) const
{
return a.f < f;
}
};
void addEdge(int from, int to, int w)
{
e[edgeNum].to = to;
e[edgeNum].w = w;
e[edgeNum].next = head[from];
head[from] = edgeNum++;
}
void dijikastra(int start)
{
int i;
memset(vis,0,sizeof(vis));
for(i = 1; i <= n; i++)
dis[i] = INF;
dis[start] = 0;
priority_queue que;
que.push(Node(0,0,start));
Node next(0,0,0);
while(!que.empty())
{
Node now = que.top();
que.pop();
if(vis[now.v]) //从集合T中选取具有最短距离的节点
continue;
vis[now.v] = true; //标记节点已从集合T加入到集合S中
for(i = 0; i < opp_Graph[now.v].size(); i++) //更新从源点到其它节点(集合T中)的最短距离
{
Edge edge = opp_Graph[now.v][i];
if(!vis[edge.to] && dis[now.v] + edge.w < dis[edge.to]) //加不加前面的判断无所谓
{
dis[edge.to] = dis[now.v] + edge.w;
next.f = dis[edge.to];
next.v = edge.to;
que.push(next);
}
}
}
}
int A_Star()
{
int i;
priority_queue que;
if(dis[start] == INF)
return -1;
que.push(Node(dis[start],0,start));
Node next(0,0,0);
while(!que.empty())
{
Node now = que.top();
que.pop();
cnt[now.v]++;
if(cnt[end] == k)
return now.f;
if(cnt[now.v] > k)
continue;
for(i = head[now.v]; i != -1; i = e[i].next)
{
next.v = e[i].to;
next.g = now.g + e[i].w;
next.f = next.g + dis[e[i].to];
que.push(next);
}
}
return -1;
}
int main()
{
int i;
int from,to,w;
edgeNum = 0;
memset(head,-1,sizeof(head));
memset(opp_Graph,0,sizeof(opp_Graph));
memset(cnt,0,sizeof(cnt));
scanf("%d %d",&n,&m);
Edge edge;
for(i = 1; i <= m; i++)
{
scanf("%d %d %d",&from,&to,&w);
addEdge(from,to,w);
edge.to = from;
edge.w = w;
opp_Graph[to].push_back(edge);
}
scanf("%d %d %d",&start,&end,&k);
if(start == end)
k++;
dijikastra(end);
int result = A_Star();
printf("%d\n",result);
return 0;
}
int uN, vN; ///切记初始化!!
bool g[MAXN][MAXN], visit[MAXN];
int xM[MAXN], yM[MAXN];
int searchpath(int u)
{
FE(v, 1, vN)
if (g[u][v] && !visit[v])
{
visit[v] = 1;
if (yM[v] == -1 || searchpath(yM[v]))
{
xM[u] = v;
yM[v] = u;
return 1;
}
}
return 0;
}
int maxmatch()
{
int ret = 0;
CLR(xM, -1);
CLR(yM, -1);
FE(u, 1, uN)
if (xM[u] == -1)
{
CLR(visit, 0);
ret += searchpath(u);
}
return ret;
}
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 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& X, vector& 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;
}
////KM算法
int W[maxn][maxn], n;
int Lx[maxn], Ly[maxn];
int Left[maxn];
bool S[maxn], T[maxn];
int in[10100], stu[10100];
bool match(int i)
{
S[i] = 1;
FE(j, 1, n) if (Lx[i] + Ly[j] == W[i][j] && !T[j])
{
T[j] = 1;
if (!Left[j] || match(Left[j]))
{
Left[j] = i;
return 1;
}
}
return 0;
}
void update()
{
int a = 1 << 30;
FE(i, 1, n) if (S[i])
FE(j, 1, n) if (!T[j])
a = min(a, Lx[i] +Ly[j] - W[i][j]);
FE(i, 1, n)
{
if (S[i]) Lx[i] -= a;
if (T[i]) Ly[i] += a;
}
}
void KM()
{
FE(i, 1, n)
{
Left[i] = Lx[i] = Ly[i] = 0;
FE(j, 1, n)
Lx[i] = max(Lx[i], W[i][j]);
}
FE(i, 1, n)
{
while (1)
{
FE(j, 1, n)
S[j] = T[j] = 0;
if (match(i)) break; else update();
}
}
}
//稳定婚姻问题
// LA3989 Ladies' Choice
#include
#include
using namespace std;
const int maxn = 1000 + 10;
int pref[maxn][maxn], order[maxn][maxn], next[maxn], future_husband[maxn], future_wife[maxn];
queue q; // 未订婚的男士队列
void engage(int man, int woman) {
int m = future_husband[woman];
if(m) {
future_wife[m] = 0; // 抛弃现任未婚夫(如果有的话)
q.push(m); // 加入未订婚男士队列
}
future_wife[man] = woman;
future_husband[woman] = man;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++)
scanf("%d", &pref[i][j]); // 编号为i的男士第j喜欢的人
next[i] = 1; // 接下来应向排名为1的女士求婚
future_wife[i] = 0; // 没有未婚妻
q.push(i);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int x;
scanf("%d", &x);
order[i][x] = j; // 在编号为i的女士心目中,编号为x的男士的排名
}
future_husband[i] = 0; // 没有未婚夫
}
while(!q.empty()) {
int man = q.front(); q.pop();
int woman = pref[man][next[man]++];
if(!future_husband[woman]) engage(man, woman); // woman没有未婚夫,直接订婚
else if(order[woman][man] < order[woman][future_husband[woman]]) engage(man, woman); // 换未婚夫
else q.push(man); // 下次再来
}
while(!q.empty()) q.pop();
for(int i = 1; i <= n; i++) printf("%d\n", future_wife[i]);
if(T) printf("\n");
}
return 0;
}
int dfs1(int u, int fa, int rt) //求点rt到 以u为根的数及其子树的最小距离
{
REP(i, SZ(tree[u]))
{
int v = tree[u][i];
if (v != fa)
dp[rt][u] = min(dp[rt][u], dfs1(v, u, rt));
}
if (fa != rt) dp[rt][u] = min(dp[rt][u], dis[rt][u]);
return dp[rt][u];
}
int dfs2(int u, int fa, int rt)
{// 求 以rt为根的数及其子树 到 以u为根的数及其子树的最小距离
int ans = dp[u][rt];
REP(i, SZ(tree[u]))
{
int v = tree[u][i];
if (v != fa)
ans = min(ans, dfs2(v, u, rt));
}
return ans;
}
void solve()
{
REP(i, n)
dfs1(i, -1, i);
REP(i, n)
{
REP(j, SZ(tree[i]))
{
int v = tree[i][j];
if (d[i][v] != INF)
continue;
d[i][v] = d[v][i] = dfs2(v, i, i);
}
}
}
//////一次dfs版本
int dfs(int u, int fa, int rt)
{//用同层节点去更新
int ans = INF;
REP(i, SZ(G[u]))
{
int v = G[u][i];
if (v != fa)
{
int t = dfs(v, u, rt);
ans = min(ans, t);
dp[u][v] = dp[v][u] = min(dp[u][v], t);
}
}
if (fa != rt) ans = min(ans, dis[rt][u]);
return ans;
}
prim();
REP(i, n)
dfs(i, -1, i);
void prim()
{
mst = 0;
REP(i, N)
dis[i] = INF, fa[i] = -1;
dis[0] = 0;
CLR(cost, 0);
int mi; int pos;
REP(i, N)
{
mi = INF;
REP(j, N)
if (!vis[j] && dis[j] < mi)
{
mi = dis[j];
pos = j;
}
mst += mi;
REP(j, N)
if (vis[j] && fa[j] != -1)
{
cost[j][pos] = max(cost[j][fa[pos]], g[fa[pos]][pos]);
cost[pos][j] = cost[j][pos];
}
if (fa[pos] != -1)
used[fa[pos]][pos] = used[pos][fa[pos]]= 1;
vis[pos] = 1;
REP(j, N)
if (!vis[j] && dis[j] > g[pos][j])
{
dis[j] = g[pos][j];
fa[j] = pos;
}
}
}
//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 G[maxn];
vector 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 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;
}
//use[u][v] = 2时,边在MST上
//use[u][v] = 1时,原图存在边。
//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;
}
hdu4085 Peach Blossom Spring
int n, m, k, st;
int mask[maxn], dp[Maxs], dis[maxn][Maxs];
bool inq[maxn][Maxs];
struct Edge{
int u, v, w;
};
vector edges;
VI G[maxn];
queue Q;
void add(int u, int v, int w)
{
edges.PB((Edge){u, v, w});
edges.PB((Edge){v, u, w});
int sz = edges.size();
G[u].PB(sz - 2);
G[v].PB(sz - 1);
}
void init()
{
CLR(dp, 0x3f), CLR(dis, 0x3f);
CLR(mask, 0), CLR(inq, 0);
RIII(n, m, k);
edges.clear();
FE(i, 0, n + 1) G[i].clear();
st = 1 << 2 * k;
REP(i, m)
{
int u, v, w;
RIII(u, v, w);
add(u, v, w);
}
}
void spfa()
{
while (!Q.empty())
{
int u = Q.front() / 10000, s0 = Q.front() % 10000;
inq[u][s0] = 0;
Q.pop();
REP(i, SZ(G[u]))
{
Edge e = edges[G[u][i]];
int v = e.v, ns = s0 | mask[e.v];
if (dis[v][ns] > dis[u][s0] + e.w)
{
dis[v][ns] = dis[u][s0] + e.w;
if (ns == s0 && !inq[v][ns])
{
inq[v][ns] = 1;
Q.push(v * 10000 + ns);
}
}
}
}
}
bool check(int s)
{
int a = 0;
REP(i, k)
{
if (s & (1 << i)) a++;
if (s & (1 << (k + i))) a--;
}
return a == 0;
}
void solve()
{
FE(i, 1, k) ////初始化
{
mask[i] = 1 << (i - 1), dis[i][mask[i]] = 0;
mask[n - i + 1] = 1 << (k + i - 1), dis[n - i + 1][mask[n - i + 1]] = 0;
}
REP(s, st) ////枚举集合
{
FE(i, 1, n)
{
for (int s0 = (s - 1) & s ; s0; s0 = (s0 - 1) & s)///枚举子集,通过子树划分转移状态
dis[i][s] = min(dis[i][s], dis[i][s0 | mask[i]]+dis[i][(s - s0) | mask[i]]);
if (dis[i][s] < INF && !inq[i][s])
{
inq[i][s] = 1;
Q.push(i * 10000 + s);
}
}
spfa(); ///spfa转移状态
}
REP(s, st)
FE(i, 1, n)
dp[s] = min(dp[s], dis[i][s]);
REP(s, st)
if (check(s))
{
for (int s0 = s & (s - 1); s0; s0 = s & (s0 - 1))
if (check(s0))
dp[s] = min(dp[s0] + dp[s - s0], dp[s]);
}
if (dp[st - 1] >= INF)
puts("No solution");
else
WI(dp[st - 1]);
}
int main()
{
int T;
RI(T);
while (T--)
{
init();
solve();
}
}
////给定一个无向图G,求它生成树的个数t
int degree[N];
LL C[N][N];
LL det(LL a[][N], int n)//生成树计数:Matrix-Tree定理
{
LL ret=1;
for(int i=1; i
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;
}
};
struct TwoSAT{
int n;
vector 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));
}
void add_clause(intx, int xval, int y, int yval)
{ /////x = xval OR y = yval 即不能同时取!val与!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;
};