AtCoder Regular Contest 093(E-Bichrome Spanning Tree)

题意:

                给出一个 N N 个点M条边的无向图,现在要给一些边染黑色或者白色,现在从这些边中选出一些边生成若干棵生成树,要求这些生成树至少包含一条白色的边和一条黑色的边,而且这些生成树的最小值是 X X ,问有多少种染色方法。

思路:

     首先对原图求一下最小生成树,计算出最小生成树的权重 w w ,那么有以下三种情况:

  1. w>X:这种情况下显然答案是  0   0

    • w=X w = X :对于这种情况,那么选出的黑色或者白色的边必然是原图最小生成树边的集合,这个只要找出所有的属于原图最小生成树的边,假设原图最小生成树中的所有边形成集合 S S ,总共有m条边,那么可以知道,不属于 S S 的边可以任意染色,属于S的边只要除去全部染成黑色和全部染成白色的就可以,答案就是 2Mm(2m1) 2 M − m ∗ ( 2 m − 1 )
    • w<X w < X :首先必须把 S S 里面的的所有边涂成黑色(或者白色),先假设全部涂成黑色,那么剩余的边,对每一条边单独处理,对边e,它染成白色添加进去之后,如果和集合 S S 中的边形成的满足题意的最小生成树权重w小于 X X ,那么这条边只能继续涂成黑色;如果w大于 X X ,它涂成黑色白色都无所谓;然后把w等于 X X 的边的数量统计下来,假设有tot条边,可以知道这 tot t o t 条边只要有一条边是白色就行了,因为只要选中这里面的一条边,剩余的边肯定都是从集合 S S 中选出来了, 不可能再从其它边中选取,如果必须涂成黑色的有d条边, 那么答案就是 2Mtotd(2tot1) 2 M − t o t − d ∗ ( 2 t o t − 1 ) ,最终结果再乘以 2 2 就行了,就是必须涂成一种颜色的有两种涂法
        
    • #include<bits/stdc++.h>
      typedef long long ll;
      const ll maxn = 1e3 + 10;
      const ll mod = 1e9 + 7;
      using namespace std;
      
      struct P {
          ll from, to, w;
          P() {}
          P(ll f, ll t, ll w) : from(f), to(t), w(w) {}
          bool operator < (P p) const { return w < p.w; }
      };
      
      ll n, m, pre[maxn], is[maxn * 2];
      vector<P> edge, G[maxn];
      ll anc[maxn][12], deep[maxn];
      ll cost[maxn][12], c[maxn], x;
      
      ll findset(ll x) { return x == pre[x] ? x : pre[x] = findset(pre[x]); }
      
      void dfs(ll x, ll fa, ll d, ll ws) {
          deep[x] = d; pre[x] = fa; c[x] = ws;
          for(ll i = 0; i < G[x].size(); i++) {
              P e = G[x][i];
              ll to = e.to, w = e.w;
              if(to == fa) continue;
              dfs(to, x, d + 1, w);
          }
      }
      
      void preprocess() {
          memset(anc, -1, sizeof anc);
          memset(cost, -1, sizeof cost);
          for(ll i = 1; i <= n; i++) {
              cost[i][0] = c[i];
              anc[i][0] = pre[i];
          }
          for(ll j = 1; (1 << j) <= n; j++) {
              for(ll i = 1; i <= n; i++) {
                  ll t = anc[i][j - 1];
                  if(t == -1) continue;
                  cost[i][j] = max(cost[i][j - 1], cost[t][j - 1]);
                  anc[i][j] = anc[t][j - 1];
              }
          }
      }
      
      ll query(ll x, ll y) {
          if(deep[x] > deep[y]) swap(x, y);
          ll lg = 0; while((1 << lg) <= deep[y]) lg++; lg--;
          ll ans = 0;
          for(ll i = lg; i >= 0; i--) {
              if(deep[y] - deep[x] >= (1 << i)) {
                  ans = max(ans, cost[y][i]);
                  y = anc[y][i];
              }
          }
          if(x == y) return ans;
          for(ll i = lg; i >= 0; i--) {
              if(anc[x][i] == anc[y][i]) continue;
              ans = max(ans, cost[x][i]);
              ans = max(ans, cost[y][i]);
              x = anc[x][i]; y = anc[y][i];
          }
          ans = max(ans, max(c[x], c[y]));
          return ans;
      }
      
      ll qmod(ll x, ll n, ll mod) {
          ll ans = 1;
          while(n) {
              if(n & 1) ans = ans * x % mod;
              x = x * x % mod;
              n >>= 1;
          }
          return ans;
      }
      
      int main() {
          while(scanf("%lld %lld", &n, &m) != EOF) {
              scanf("%lld", &x);
              memset(is, 0, sizeof is);
              for(ll i = 0; i < maxn; i++) {
                  G[i].clear(); pre[i] = i;
              }
              edge.clear();
              for(ll i = 0; i < m; i++) {
                  ll u, v, w;
                  scanf("%lld %lld %lld", &u, &v, &w);
                  edge.push_back(P(u, v, w));
              }
              sort(edge.begin(), edge.end());
              ll tot_wight = 0, ed = 0;
              for(ll i = 0; i < edge.size(); i++) {
                  P e = edge[i];
                  ll u = e.from, v = e.to, w = e.w;
                  ll nu = findset(u), nv = findset(v);
                  if(nu == nv) continue;
                  is[i] = 1; ed++;
                  tot_wight += w; pre[nu] = nv;
                  G[u].push_back(P(u, v, w));
                  G[v].push_back(P(v, u, w));
              }
              dfs(1, 0, 1, 0); preprocess();
              for(ll i = 0; i < edge.size(); i++) {
                  if(is[i]) continue;
                  ll from = edge[i].from, to = edge[i].to;
                  ll max_w = query(from, to);
                  if(max_w == edge[i].w) { is[i] = 1; ed++; }
              }
      
              ll ans;
              if(tot_wight > x) ans = 0;
              else if(tot_wight == x) {
                  ans = qmod(2, m - ed, mod);
                  ans = ans * (qmod(2, ed, mod) - 2) % mod;
                  if(ans < 0) ans = ans + mod;
              } else {
                  ll dx = 0;
                  for(ll i = 0; i < edge.size(); i++) {
                      if(is[i]) continue;
                      ll max_w = query(edge[i].from, edge[i].to);
                      if(tot_wight - max_w + edge[i].w == x) dx++;
                      else if(tot_wight - max_w + edge[i].w < x) ed++;
                  }
                  ans = qmod(2, m - ed - dx, mod);
                  ll col = 2 * (qmod(2, dx, mod) - 1) % mod;
                  ans = ans * col % mod;
                  if(ans < 0) ans = ans + mod;
              }
              cout << ans << endl;
          }
          return 0;
      }
      
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值