ferin blog

Sep 1, 2018 - 2 minute read - category1 category2

ARC093 E - Bichrome Spanning Tree

問題ページ

橋は全域木をつくるのに確実に使うので二重編連結成分分解して連結成分ごとにDPとか考えたけどX<=10^12でDPの条件を持てないし無理。色々考えたけどわからないので解説を見た。 https://img.atcoder.jp/arc093/editorial.pdf https://www.youtube.com/watch?v=WFg2yJGZ2Cw

ある条件の全域木を求めるときに最小全域木との差分から考える。
ある辺e=(u,v,w)を含む条件下でのMSTの重みは w-maxpath(u,v) を本来のMSTに加算したものになる。maxpath(u,v)=(MSTのパスu~vの辺の中で最大の辺の重み)
条件がXのものを数えるとき、f(X)=(条件がX以下のものの数)を求めてans=f(X)-f(X-1)とする。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
#define int ll
using PII = pair<int, int>;
template <typename T> using V = vector<T>;
template <typename T> using VV = vector<V<T>>;
template <typename T> using VVV = vector<VV<T>>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define PB push_back

const ll INF = (1LL<<60);
const int MOD = 1000000007;

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template <typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }
template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
  out<<'('<<a.first<<','<<a.second<<')';
  return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
  out<<'[';
  REP(i, a.size()) {out<<a[i];if(i!=a.size()-1)out<<',';}
  out<<']';
  return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

class UnionFind {
public:
  const static int MAX_N = 100010;
  int par[MAX_N];
  int s[MAX_N];
  UnionFind() { init(); }
  UnionFind(int n) { init(n); }
  void init() { for(int i=0; i<MAX_N; ++i) par[i] = i, s[i] = 1; }
  void init(int n) { for(int i=0; i<n; ++i) par[i] = i, s[i] = 1; }
  int find(int x) {
    if(par[x] == x) return x;
    return par[x] = find(par[x]);
  }
  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if(x == y) return;
    if(s[x] < s[y]) par[x] = y, s[y] = s[x] + s[y];
    else par[y] = x, s[x] = s[x] + s[y];
  }
  bool same(int x, int y) { return find(x) == find(y); }
  int size(int x) { return s[find(x)]; }
};
UnionFind uf;

ll binpow(ll x, ll e, ll mo=MOD) {
  ll a = 1, p = x;
  while(e > 0) {
    if(e%2 == 0) {p = (p*p) % mo; e /= 2;}
    else {a = (a*p) % mo; e--;}
  }
  return a;
}

signed main(void)
{
  cin.tie(0);
  ios::sync_with_stdio(false);

  int n, m, x;
  cin >> n >> m >> x;
  VV<int> e(m, V<int>(3));
  REP(i, m) cin >> e[i][1] >> e[i][2] >> e[i][0], e[i][1]--, e[i][2]--;
  
  map<int, int> mp;
  sort(ALL(e));
  REP(i, m) {
    uf.init(n);
    uf.unite(e[i][1], e[i][2]);
    int ret = e[i][0];
    REP(j, m) {
      if(i==j) continue;
      if(!uf.same(e[j][1], e[j][2])) {
        uf.unite(e[j][1], e[j][2]);
        ret += e[j][0];
      }
    }
    mp[ret]++;
  }

  for(auto i=next(mp.begin()); i!=mp.end(); i++) {
    i->second += prev(i)->second;
  }

  auto f = [&](int x) {
    auto itr = mp.upper_bound(x);
    int c;
    if(itr == mp.begin()) c = 0;
    else itr--, c = itr->second;
    if(c == 0) return binpow(2, m);
    return binpow(2, m-c+1);
  };

  cout << (((f(x-1) - f(x)) % MOD) + MOD) % MOD << endl;

  return 0;
}