kuangbin專題八生成樹總結
總結:生成樹的知識點真多,不過博主覺得圖論的題目終究是建圖難,第一步就是如何建圖,將其轉換成已知的問題。
另外,關於生成樹的兩個注意點,也是為了防止碰到毒瘤題。一就是自環,二就是重邊。
A - The Unique MST
次小生成樹裸題。
prim模板
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int map[maxn][maxn];
int lowcost[maxn];
int closest[maxn];
bool used[maxn][maxn];
bool vis[maxn];
int Max[maxn][maxn];
int n, m;
int prim()
{
memset(lowcost, inf, sizeof(lowcost));
memset(closest, 0, sizeof(closest));
memset(used, 0, sizeof(used));
memset(vis, 0, sizeof(vis));
memset(Max, 0 , sizeof(Max));
lowcost[1] = 0;
vis[1] = 1;
closest[1] = 1;
int num = 0, ans = 0;
int nownode = 1;
while (num<n - 1)
{
int Mincost = inf, theidx;
for (int i = 1;i <= n;i++)
{
if (vis[i])
{
if (i == nownode)continue ;
Max[i][nownode] = Max[nownode][i] = max(Max[i][closest[nownode]], lowcost[nownode]);
}
else
{
if (lowcost[i] > map[i][nownode])
{
lowcost[i] = map[i][nownode];
closest[i] = nownode;
}
if (Mincost > lowcost[i])
{
Mincost = lowcost[i];
theidx = i;
}
}
}
ans += Mincost;
num++;
nownode = theidx;
vis[nownode] = 1;
used[theidx][closest[theidx]] = used[closest[theidx]][theidx] = 1;
}
for (int i = 1;i <= n;i++)
Max[i][nownode] = Max[nownode][i] = max(Max[i][closest[nownode]], lowcost[nownode]);
return ans;
}
void solve(int num)
{
for (int i = 1;i <= n;i++)
for (int j = i + 1;j <= n;j++)if (!used[i][j])
{
int ans = num - Max[i][j] + map[i][j];
if (ans == num)
{
puts("Not Unique!");
return;
}
}
printf("%d\n", num);
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &n, &m);
int u, v, dis;
memset(map, inf, sizeof(map));
for (int i = 1;i <= m;i++)
{
scanf("%d %d %d", &u, &v, &dis);
map[u][v] = map[v][u] = dis;
}
int ans = prim();
solve(ans);
}
}
D - Is There A Second Way Left?
這題相對於上題來說,有重邊。所以用kruskal(個人感覺也能用prim)。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cmath>
#include<climits>
using namespace std;
#define ll long long
const int maxn = 105;
const int maxm = 205;
const int inf = 0x3f3f3f3f;
struct Edge
{
int u, v, dis;
bool vis;
Edge(int _u, int _v, int _d) :u(_u), v(_v), dis(_d) { vis = 0; }
Edge(){}
bool operator<(const Edge &b)const
{
return dis < b.dis;
}
}edges[maxm];
int tot;
int n, m;
vector<int>node[maxn];
void addedge(int u, int v, int dis)
{
edges[tot++] = Edge(u, v, dis);
}
int f[maxn];
int find(int a)
{
return a == f[a] ? a : f[a] = find(f[a]);
}
int Max[maxn][maxn];
int Kruskal()
{
sort(edges + 1, edges + tot);
for (int i = 1;i <= n;i++)
f[i] = i, node[i].clear(), node[i].push_back(i);
memset(Max, 0, sizeof(Max));
int ans = 0, tot1 = 0;
for (int i = 1;i < tot;i++)
{
if (tot1 == n - 1)break;
int u = edges[i].u, v = edges[i].v, dis = edges[i].dis;
int fu = find(u), fv = find(v);
if (fu != fv)
{
edges[i].vis = 1;
tot1++;
ans += dis;
f[fv] = fu;
//cout << find(fu) << endl;
int Sizefu = node[fu].size();
int Sizefv = node[fv].size();
for (int j = 0;j < Sizefu;j++)
for (int k = 0;k < Sizefv;k++)
Max[node[fu][j]][node[fv][k]] = Max[node[fv][k]][node[fu][j]] = dis;
int tmp[105];
for (int j = 0;j < Sizefu;j++)
tmp[j] = node[fu][j];
for (int j = 0;j < Sizefv;j++)
node[fu].push_back(node[fv][j]);
for (int j = 0;j < Sizefu;j++)
node[fv].push_back(tmp[j]);
}
}
if (tot1 != n - 1)return -1;
return ans;
}
int solve(int num)
{
int ans = inf;
for (int i = 1;i <tot;i++)
if (!edges[i].vis)
ans = min(ans, num - Max[edges[i].u][edges[i].v] + edges[i].dis);
if (ans == inf)
return -1;
return ans;
}
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
tot = 1;
scanf("%d %d", &n, &m);
int u, v, dis;
for (int i = 1;i <= m;i++)
{
scanf("%d %d %d", &u, &v, &dis);
addedge(u, v, dis);
}
printf("Case #%d : ", cas++);
int ans1 = Kruskal();
if (ans1 == -1)
puts("No way");
else
{
int ans2 = solve(ans1);
if (ans2 == -1)
puts("No second way");
else
printf("%d\n", ans2);
}
}
}
另外一種寫法就是先跑kruskal,然後刪掉一個邊(使邊兩點並查集一樣即可),然後再跑kruskal。這裡就不寫了。。
接下來就是最小樹形圖問題,也就是有向圖的最小生成樹。這裡就是朱劉演算法的使用。
這裡有2個問題需要說明。
1,朱劉演算法的模板有兩份,一份是矩陣存圖,一種是存邊。有的時候用其中一份會超時,這時候就得自行判斷一下用哪個了。例如下面的G。
2,有向圖的最小生成樹,有的時候會指定根,那麼這個時候直接套就行了。如果沒有指定根的話,叫你輸出根,這時候就需要建圖方法了。首先先記錄所有邊權和為sum,然後建一個虛點,虛點連向所有點,邊權為sum+1,然後跑朱劉演算法,如果跑出來的ans>=2*sum+2,就說明圖不連通。如何求出的根呢,假設原有邊有m條,虛點為s,我們在選邊(假設邊下標為num)的時候,如果選擇到的邊有一個點是s的話,那麼更新root=num-m。這個也比較好理解。
F - Teen Girl Squad
矩陣存圖版本
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1005;
const int INF = 1e9;
bool vis[maxn];
bool flag[maxn];
int w[maxn][maxn];
int pre[maxn];
int n, m;
void init()//不能少了初始化的內容
{
memset(vis, 0, sizeof(vis));
memset(flag, 0, sizeof(flag));
for (int i = 0; i <= n; i++)
{
w[i][i] = INF;
for (int j = i + 1; j <= n; j++)
w[i][j] = w[j][i] = INF;
}
}
int directed_mst(int u)//u表示根節點
{
int ans = 0;
memset(vis, 0, sizeof(vis));
while (true)
{
//求最短弧集合E
for (int i = 1; i <= n; i++)if (i != u && !flag[i])
{
w[i][i] = INF, pre[i] = i;
for (int j = 1; j <= n; j++)if (!flag[j] && w[j][i]<w[pre[i]][i])
{
pre[i] = j;
}
if (pre[i] == i)return -1;//也可以用dfs預處理判斷凸的連通
}
//判斷E是否有環
int i;
for (i = 1; i <= n; i++)
{
if (i != u && !flag[i])
{
int j = i, cnt = 0;
while (j != u && pre[j] != i && cnt <= n) j = pre[j], ++cnt;
if (j == u || cnt>n) continue; //最後能找到起點(根)或者是走過的點已經超過了n個,表示沒有有向環
break;
}
}
if (i>n)
{
for (int i = 1; i <= n; i++)if (i != u && !flag[i]) ans += w[pre[i]][i];
return ans;
}
//有環,進行收縮,把整個環都收縮到一個點i上。
int j = i;
memset(vis, 0, sizeof(vis));
do
{
ans += w[pre[j]][j], j = pre[j], vis[j] = flag[j] = true;//對環內的點標記,並且直接對環的權值進行加和記錄,在最後找到最小樹形圖之後就不用展開收縮點了
} while (j != i);
flag[i] = false; // 環縮成了點i,點i仍然存在
//收縮點的同時,對邊權值進行改變
for (int k = 1; k <= n; ++k)if (vis[k]) // 在環中點點
{
for (int j = 1; j <= n; j++)if (!vis[j]) // 不在環中的點
{
if (w[i][j] > w[k][j]) w[i][j] = w[k][j];
if (w[j][k]<INF && w[j][k] - w[pre[k]][k] < w[j][i])
w[j][i] = w[j][k] - w[pre[k]][k];
}
}
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
scanf("%d %d", &n, &m);
init();
int u, v,dis;
for (int i = 1;i <= m;i++)
{
scanf("%d %d %d", &u, &v, &dis);
w[u+1][v+1] = min(w[u+1][v+1],dis);
}
printf("Case #%d: ", cas++);
int ans = directed_mst(1);
if (ans == -1)
puts("Possums!");
else
printf("%d\n", ans);
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 1010
#define INF 0x7f7f7f7f
struct Edge
{
int u,v,w;
} e[N*N];
int cnt;
int in[N];
int vis[N],pre[N],id[N];
int minroot;
void addedge(int u,int v,int w)
{
e[cnt].u=u;
e[cnt].v=v;
e[cnt++].w=w;
}
int Directed_MST(int root,int NV,int NE)
{
int ret = 0;
while(true)
{
///步驟1:找到最小邊
for(int i = 0; i < NV; i ++)
in[i] = INF;
memset(pre,-1,sizeof(pre));
for(int i = 0; i < NE; i ++)
{
int u = e[i].u , v = e[i].v;
if(e[i].w < in[v] && u != v)
{
pre[v] = u;
in[v] = e[i].w;
if(u==root) minroot=i;
}
}
for(int i = 0; i < NV; i ++)
{
if(i == root) continue;
if(in[i] == INF) return -1;///除了根節點以外有點沒有入邊,則根無法到達他
}
int cntnode = 0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
///找環
in[root] = 0;
for(int i = 0; i < NV; i ++) ///標記每個環,編號
{
ret += in[i];
int v = i;
while(vis[v] != i && id[v] == -1 && v != root)
{
vis[v] = i;
v = pre[v];
}
if(v != root && id[v] == -1)
{
for(int u = pre[v]; u != v; u = pre[u])
{
id[u] = cntnode;
}
id[v] = cntnode ++;
}
}
if(cntnode == 0) break;//無環
for(int i = 0; i < NV; i ++)
if(id[i] == -1)
id[i] = cntnode ++;
///步驟3:縮點,重新標記
for(int i = 0; i < NE; i ++)
{
int u=e[i].u;
int v = e[i].v;
e[i].u = id[u];
e[i].v = id[v];
if(e[i].u != e[i].v) e[i].w -= in[v];
}
NV = cntnode;
root = id[root];
}
return ret;///最小樹形圖的長度
}
int main()
{
int n,m,sum;
int u,v,w;
while(scanf("%d %d",&n,&m)!=EOF)
{
cnt=0;sum=0;
for(int i=0; i<m; i++)
{
scanf("%d %d %d",&u,&v,&w);
addedge(u+1,v+1,w);
sum+=w;
}
sum++;
for(int i=1; i<=n; i++)
addedge(0,i,sum);
int ans=Directed_MST(0,n+1,cnt);
if(ans==-1||ans>=2*sum)
printf("impossible\n\n");
else
printf("%d %d\n\n",ans-sum,minroot-m);
}
return 0;
}
接下來就是生成樹計數問題,做這個問題,首先你得要有一個矩陣模板。
這裡說明一下矩陣的一個問題。
有的時候需要模一個數,那麼矩陣求行列式就只能用輾轉相除法,否則就能用高精度。
生成樹計數目前碰到的一個問題,就是限制邊權最小的生成樹計數,也就是最小生成樹計數,這個有點難,稍後會解釋。
L - Lightning
隨便貼一個題來展示博主的模板。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cmath>
#include<climits>
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-10;
const int maxn = 305;
const int mod = 10007;
int sgn(ld x)
{
if (fabs(x) < eps)return 0;
else if (x > 0)return 1;
return -1;
}
struct M
{
int n, m;
ll a[maxn][maxn];
M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
void pri()
{
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= m;j++)cout << a[i][j] << ' ';
cout << endl;
}
}
friend M operator*(M a, M b)
{
M c;
for (int k = 1;k <= a.m;k++)
for (int i = 1;i <= a.n;i++)
for (int j = 1;j <= b.m;j++)
c.a[i][j] += a.a[i][k] * b.a[k][j];
return c;
}
friend M operator-(M a, M b)
{
for (int i = 1;i <= a.n;i++)
for (int j = 1;j <= a.m;j++)
a.a[i][j] -= b.a[i][j];
return a;
}
void make_I(int _n)
{
n = m = _n;
memset(a, 0, sizeof(a));
for (int i = 1;i <= n;i++)a[i][i] = 1;
}
//行列式高精度
long double mat[maxn][maxn], tmp[maxn];
long double det()
{
long double ans = 1;
for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) mat[i][j] = a[i][j];
for (int i = 1;i <= n;i++)
{
int pos = i;
while (fabs(mat[pos][i])<eps&&pos<n) ++pos;
if (fabs(mat[pos][i])<eps) return 0;
if (pos^i)
{
copy(mat[pos] + 1, mat[pos] + 1 + m + 1, tmp + 1);
copy(mat[i] + 1, mat[i] + 1 + m + 1, mat[pos] + 1);
copy(tmp + 1, tmp + 1 + m + 1, mat[i] + 1);
}
ans *= mat[i][i];
for (int j = i + 1;j <= n;j++)
{
long double p = mat[j][i] / mat[i][i];
for (int k = i;k <= m;k++) mat[j][k] -= mat[i][k] * p;
}
}
return ans;
}
//行列式輾轉相除法
ll det(ll mod)
{
ll ret = 1;
for (int i = 1;i <= n;i++)
{
if (a[i][i] < 0)
{
ret = -ret;
for (int k = i;k <= n;k++)a[i][k] = -a[i][k];
}
for (int j = i + 1;j <= n;j++)
{
for (int k = i;k <= n;k++)a[i][k] %= mod, a[j][k] %= mod;
while (a[j][i])
{
if (a[j][i] < 0)
{
ret = -ret;
for (int k = i;k <= n;k++)a[j][k] = -a[j][k];
}
ll t = a[i][i] / a[j][i];
for (int k = i;k <= n;k++)a[i][k] = (a[i][k] - t*a[j][k]) % mod;
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] % mod;
}
return (ret + mod) % mod;
}
}A, C, D;
struct node
{
int x, y;
node(int _x,int _y):x(_x),y(_y){}
node(){}
}nodes[maxn];
double map[maxn][maxn];
int N, R;
double dist(int a, int b)
{
double x = nodes[a].x - nodes[b].x;
double y = nodes[a].y - nodes[b].y;
return sqrt(x*x + y*y);
}
vector<int>V[maxn];
bool check(int a, int b)
{
for(auto it:V[a])if(it!=b)
if (!sgn(map[a][it] + map[it][b] - map[a][b]))
return 0;
return 1;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &N, &R);
for (int i = 1;i <= N;i++)V[i].clear();
A.mem(N);
C.mem(N);
D.mem(N);
int x, y;
for (int i = 1;i <= N;i++)
{
scanf("%d %d", &x, &y);
nodes[i] = node(x, y);
}
for(int i=1;i<=N;i++)
for (int j = i+1;j <= N;j++)if (i != j)
{
double tmp = dist(i, j);
map[i][j] = map[j][i] = tmp;
if (tmp <= R)
V[i].push_back(j), V[j].push_back(i);
}
for(int i=1;i<=N;i++)
for (int j = i + 1;j <= N;j++)if (map[i][j] <= R)
{
if (check(i, j))
{
A.a[i][j] = A.a[j][i] = 1;
D.a[i][i]++;
D.a[j][j]++;
}
}
for (int i = 1;i <= N;i++)
for (int j = 1;j <= N;j++)
C.a[i][j] = D.a[i][j] - A.a[i][j];
//C.pri();
C.n--;C.m--;
int ans = C.det(mod);
printf("%d\n", ans == 0 ? -1 : ans);
}
}
M - Minimum Spanning Tree
如何計算最小生成樹個數呢?
首先我們回顧一下kruskal演算法。
kruskal演算法首先將邊按照邊權排序,假設邊權先有一堆c1,然後c2,然後c3…(c1< c2< c3).
我們首先會算c1的結果,而計算c2時,前面的結果實際和現在算c2是互不干擾的。也就是說,我們在計算最小生成樹時,可以把它分成若干個階段。在算c1階段時,我們可以算它的生成樹個數,然後再算c2時,再算它的生成樹個數,然後相乘就是答案了。
知道思路了,再說一個比較混的點。
設fa[maxn]為目前的連通狀態,ka[maxn]為更新後的連通狀態。在找邊的時候,不能更新fa,而只能更新ka,在計算完生成樹後,再更新fa。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-10;
const int maxn = 105;
const int maxm = 1005;
const int mod = 1e9;
int sgn(ld x)
{
if (fabs(x) < eps)return 0;
else if (x > 0)return 1;
return -1;
}
struct M
{
int n, m;
ll a[maxn][maxn];
M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
void pri()
{
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= m;j++)cout << a[i][j] << ' ';
cout << endl;
}
}
friend M operator*(M a, M b)
{
M c;
for (int k = 1;k <= a.m;k++)
for (int i = 1;i <= a.n;i++)
for (int j = 1;j <= b.m;j++)
c.a[i][j] += a.a[i][k] * b.a[k][j];
return c;
}
friend M operator-(M a, M b)
{
for (int i = 1;i <= a.n;i++)
for (int j = 1;j <= a.m;j++)
a.a[i][j] -= b.a[i][j];
return a;
}
void make_I(int _n)
{
n = m = _n;
memset(a, 0, sizeof(a));
for (int i = 1;i <= n;i++)a[i][i] = 1;
}
//行列式高精度
long double mat[maxn][maxn], tmp[maxn];
long double det()
{
long double ans = 1;
for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) mat[i][j] = a[i][j];
for (int i = 1;i <= n;i++)
{
int pos = i;
while (fabs(mat[pos][i])<eps&&pos<n) ++pos;
if (fabs(mat[pos][i])<eps) return 0;
if (pos^i)
{
copy(mat[pos] + 1, mat[pos] + 1 + m + 1, tmp + 1);
copy(mat[i] + 1, mat[i] + 1 + m + 1, mat[pos] + 1);
copy(tmp + 1, tmp + 1 + m + 1, mat[i] + 1);
}
ans *= mat[i][i];
for (int j = i + 1;j <= n;j++)
{
long double p = mat[j][i] / mat[i][i];
for (int k = i;k <= m;k++) mat[j][k] -= mat[i][k] * p;
}
}
return ans;
}
//行列式輾轉相除法
ll det(ll mod)
{
ll ret = 1;
for (int i = 1;i <= n;i++)
{
if (a[i][i] < 0)
{
ret = -ret;
for (int k = i;k <= n;k++)a[i][k] = -a[i][k];
}
for (int j = i + 1;j <= n;j++)
{
for (int k = i;k <= n;k++)a[i][k] %= mod, a[j][k] %= mod;
while (a[j][i])
{
if (a[j][i] < 0)
{
ret = -ret;
for (int k = i;k <= n;k++)a[j][k] = -a[j][k];
}
ll t = a[i][i] / a[j][i];
for (int k = i;k <= n;k++)a[i][k] = (a[i][k] - t*a[j][k]) % mod;
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] % mod;
}
return (ret + mod) % mod;
}
}A, C;
struct Edge
{
int u, v, dist;
Edge(int _u, int _v, int _d) :u(_u), v(_v), dist(_d) {}
Edge() {}
bool operator<(const Edge &b)const
{
return dist < b.dist;
}
}edges[maxm];
int n, m, p;
int find(int a, int *f) { return a == f[a] ? a : f[a] = find(f[a], f); }
int f[maxn], ka[maxn];
bool vis[maxn];
vector<int>V[maxn];
ll matrix_tree()
{
for (int i = 1;i <= n;i++)if (vis[i])
V[find(i, ka)].push_back(i);
memset(vis, 0, sizeof(vis));
ll ans = 1;
for (int i = 1;i <= n;i++)if (V[i].size())
{
int Size = V[i].size();
C.mem(Size);
for (int j = 0;j<Size;j++)
for (int k = j + 1;k < Size;k++)
{
int u = V[i][j], v = V[i][k];
C.a[j + 1][k + 1] = 0 - A.a[u][v];
C.a[k + 1][j + 1] = 0 - A.a[v][u];
C.a[j + 1][j + 1] += A.a[u][v];
C.a[k + 1][k + 1] += A.a[u][v];
}
C.n--, C.m--;
ans = ans*C.det(p) % p;
V[i].clear();
}
for (int i = 1;i <= n;i++)
f[find(i, f)] = find(i, ka);
return ans;
}
void solve()
{
sort(edges + 1, edges + 1 + m);
for (int i = 1;i <= n;i++)f[i] = ka[i] = i;
int now = edges[1].dist;
int u, v, fu, fv;
ll ans = 1;
for (int i = 1;i <= m;i++)
{
u = edges[i].u, v = edges[i].v;
fu = find(u, f), fv = find(v, f);
if (fu != fv)
{
ka[find(fu, ka)] = find(fv, ka);
A.a[fu][fv]++;
A.a[fv][fu]++;
vis[fu] = 1, vis[fv] = 1;
}
if (i == m || now != edges[i + 1].dist)
{
ans = ans*matrix_tree() % p;
now = edges[i + 1].dist;
}
}
for (int i = 2;i <= n;i++)if (ka[i] != ka[i - 1])ans = 0;
printf("%lld\n", ans%p);
}
int main()
{
while (~scanf("%d %d %d", &n, &m, &p) && n)
{
A.mem(n);
int u, v, dis;
for (int i = 1;i <= m;i++)
{
scanf("%d %d %d", &u, &v, &dis);
edges[i] = Edge(u, v, dis);
}
solve();
}
}
這題有點毒瘤的地方是,如果我不在printf那裡模p的話,竟然會wa。至今不知道原因。。
之後會補充對朱劉演算法和矩陣樹的理解。
相關推薦
kuangbin專題八生成樹總結
總結:生成樹的知識點真多,不過博主覺得圖論的題目終究是建圖難,第一步就是如何建圖,將其轉換成已知的問題。 另外,關於生成樹的兩個注意點,也是為了防止碰到毒瘤題。一就是自環,二就是重邊。 A - The Unique MST 次小生成樹裸題。 prim模板
kuangbin帶你飛 專題八 生成樹
HDU 4081(次小生成樹) 題意:這題是給你n個點座標,m條邊,然後讓你連成一棵生成樹,可以把其中一條邊的權值變為0,然後求這條邊兩端點的權值之和/(生成樹權值-這條邊權)最大值 題解:這得列舉刪除每一條邊啊,然後求含有這條邊的最小生成樹 看了題解之後才明白這是求次小生
[kuangbin帶你飛]專題八 生成樹 題解彙總
本來準備省賽和其他的比賽先不打算做kuangbin專題了,不過我朋友AK了最小生成樹專題,問我繼續怎麼練。既然都練了最小生成樹,不如把後面的那個生成樹專題也啃了吧。然後clone生成樹專題,花了5天懟完。主要是中間一堆作業要懟,耽誤了很多時間。我把我的題解和程式碼都放到了Gi
kuangbin專題八 UVA10766 (生成樹計數)Organising the Organisation(請無視這篇文章)
題意: 給出n,m,k,代表一家公司有n個部門,編號1到n,有m組關係,表示i和j不能直接聯通,k代表主管部門,問你有多少種分層方案。另外,這道題的k可以忽略掉,所以他的範圍完全是嚇唬人的。 題解: 抱歉,這道題我真的無法弄的通俗的說出來
kuangbin專題八 URAL1627 Join(生成樹計數)
題意: 給出一個圖,’.’表示臥室,’*’表示儲藏間,每個格子上下左右都有一堵牆,然後需要打通一些臥室的牆(只能是相鄰房間才能打通)使得臥室之間聯通的方案數. 給每個臥室編個號,給可以打通的臥室加邊,就是裸的生成樹計數了. 題解: 打通一
stp生成樹總結
mst 中斷 同時 ces 交換 信息 臨時 速度 動作 stp生成樹協議目前主要分stp、rstp、mstp三類,依次向下兼容。 1、涉及的概念: stp:根交換(跟網橋)、根端口、指定端口、可選端口,bpdu保護、root保護、收斂慢,單樹。 rstp:根交換、
最小生成樹 kuangbin專題最後一個題
題目連結:https://cn.vjudge.net/contest/66965#problem/N 註釋:這道題需要用krustra,用prim的話可能會超時。並且在計算距離的時候要儘量減少步驟,具體注意事項在程式碼中說吧。 AC程式碼: #include<iostream&
12.6日總結 最小度限制生成樹 POJ 1639(含聯通塊dfs劃分模板)
今天看了 一道 有度數限制的最小生成樹題目,按照書上給出的思路寫的程式碼,思路的最後一步沒有實現,可能沒弄懂書上的意思。 http://poj.org/problem?id=1639。 題意: 大概是說在一張無向圖中
企業三層架構、冗餘、STP生成樹協議總結
總結 1.企業三層架構 2. 冗餘(線路冗餘+裝置冗餘) 3. STP生成樹協議:IEEE802.1D,PVST+,802.1W,RSTP(rpvst),802.1S(MST) 企業三層架構(內網結構) 接入層: 常使用二層交換機,就近提供介面密度,用於使用者的接入 匯聚層(分佈層):
kuangbin專題十二基礎dp總結
做這個專題的時候感覺好迷。一度被題噁心到了。。 這題把所有不是獨立思考做出來的題貼出來吧。 A - Max Sum Plus Plus 題解:dp[i][j] 代表前i個數在必須選第i個的前提下組成j組的最大值。 那麼方程為: dp[i][j]=ma
kuangbin專題十七AC自動機總結
這個專題寫的我頭皮發麻,出現了好多小bug耗費了我好多時間,但總體看不算太難,只要把思路縷清就行了。 AC自動機的題目有兩類,一類是字串找子串個數的,另一類則是建立狀態,然後進行dp或者矩陣快速冪。 B - 病毒侵襲 這題不算太難,但有一個坑點,就是字元都
生成樹計數總結 SPOJ104
可看周冬的論文《生成樹的計數及其應用》,利用Matrix-Tree定理解決生成樹計數的問題,複雜度是矩陣乘法的複雜度O(n^3)。 總結: 無向圖,允許有重邊。 四個重要矩陣A(鄰接矩陣),D(度數矩陣),C(KirchHoff矩陣,C=D-A),B(關聯矩陣,B其實是用來
STP生成樹協議和VTP
primary server flash 交換機 優先級 STP#show spanning-tree bri 查看STP生成樹SW conf#spanning-tree vlan 1 priority root 修改優先級為根,可以改成數字,0為最低SW conf#spanning-tr
3950雙層交換機生成樹協議
交換機 雙層 3950雙層交換機,A/B交換機分別開啟生成樹協議。spanning-tree查看端口 show spanning-tree兩臺交換機開啟生成樹協議之後廣播風暴消失。交換機A 1/1、1/2、1/24端口交換機B 1/3、1/4、1/23端口燈不再爆閃本文出自 “11921400” 博客
[POJ 2728]Desert King(0-1分數規劃/最優比率生成樹)
eat ice finall nec clu bool ann channels try Description David the Great has just become the king of a desert country. To win the respec
生成樹保護功能
生成樹 保護功能 1、配置 BPDU 保護功能system-view stp bpdu-protection //請在有邊緣端口的設備上進行如下配置2、配置根保護功能system-viewinterface ge 0/0/1stp root-protection3、配置環路保護功能system
生成樹中的5種交換機端口狀態和3種生成樹協議模式
style blocking 用戶數 forward 命令 learn 用戶數據 pvst+ class 端口狀態:①關閉(disable):端口處於管理關閉狀態 即DIS②阻塞(blocking): 不能轉發用戶數據 即BLK③監聽(listening): 接口開始啟動
【中山市選2010】【BZOJ2467】生成樹
online 中心 什麽是 var cti spl scrip scanf load Description 有一種圖形叫做五角形圈。一個五角形圈的中心有1個由n個頂點和n條邊組成的圈。在中心的這個n邊圈的每一條邊同一時候也是某一個五角形的一條邊,一共
貨車運輸(最大生成樹+LCA)
整數 fine std 一個 ext getchar() 最小路徑 ont getch 題目描述 A 國有 n 座城市,編號從 1 到 n,城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q 輛貨車在運輸貨物, 司機們想知道每輛車在不超過車輛限重
uva10766生成樹計數
als mes art 算子 技術分享 math 個數 main mat 此類題是給定一個無向圖,求所有生成樹的個數,生成樹計數要用到Matrix-Tree定理(Kirchhoff矩陣-樹定理) G的度數矩陣D[G]是一個n*n的矩陣,並且滿足:當i≠j時,dij=0;當i