CF915D Almost Acyclic Graph

签到题。

考虑暴力删边,拓扑排序判环,时间复杂度:O(m×(n+m))O(m\times(n+m))。会炸。

注意到拓扑排序的性质:不关心具体删了哪条边,只关心入度。

所以暴力删边变成暴力减度数。时间复杂度:O(n×(n+m))O(n\times (n+m))

#include<bits/stdc++.h>
  
using namespace std;
const int MAXN = 505;
int n, m;
vector<int> a[MAXN];
bool check(vector<int> ind){
  int cnt = 0;
  queue<int> b;
  for (int i = 1; i <= n; i++){
    if (!ind[i]){
      b.push(i);
    }
  }
  while (b.size()){
    auto x = b.front();
    b.pop();
    cnt++;
    for (auto v : a[x]){
      if (!--ind[v]){
        b.push(v);
      }
    }
  }
  return cnt == n;
}
int main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  vector<int> ind(n + 1, 0);
  while (m--){
    int u, v;
    cin >> u >> v;
    a[u].push_back(v);
    ind[v]++;
  }
  if (check(ind)){
    cout << "YES";
    return 0;
  }
  for (int i = 1; i <= n; i++){
    if (!ind[i]){
      continue;
    }
    ind[i]--;
    if (check(ind)){
      cout << "YES";
      return 0;
    }
    ind[i]++;
  }
  cout << "NO";
  return 0;
}

CF543B Destroying Roads

直接从每个点出发跑一边 Dijkstra。

因为要破坏尽可能多的公路,那么我们就要让两个点走过的路径的集合的大小尽可能小。

那么就有可能是以下四种情况(其中,xx 代表某个中间节点,disi,jdis_{i,j} 表示从 iijj 的最短路):

ans=min{diss1,t1+diss2,t2diss1,x+disx,t1+diss2,t2diss1,t1+diss2,x+disx,t2diss1,x+disx,t2+diss2,x+disx,t2ans=\min\begin{cases} dis_{s1,t1}+dis_{s2,t2}\\ dis_{s1,x}+dis_{x,t1}+dis_{s2,t2}\\ dis_{s1,t1}+dis_{s2,x}+dis_{x,t2}\\ dis_{s1,x}+dis_{x,t2}+dis_{s2,x}+dis_{x,t2} \end{cases}

在不超过规定时间 tit_i 的情况下,n2n^2 更新答案即可。

时间复杂度:O(n×mlogn)O(n\times m\log n)

#include<bits/stdc++.h>


using namespace std;
const int MAXN = 3e3 + 5;
int n, m, dis[MAXN][MAXN];
struct Node{
  int x, w;
  bool operator<(const Node &i)const{
    return w > i.w;
  }
}; 
vector<Node> a[MAXN];
int s1, t1, l1, s2, t2, l2;
void dij(int x, int dis[]){
  priority_queue<Node> b;
  for (int i = 1; i <= n; i++){
    dis[i] = 1e9; 
  }
  dis[x] = 0;
  b.push({x, 0});
  while (b.size()){
    auto x = b.top();
    b.pop();
    if (x.w > dis[x.x]){
      continue;
    }
    for (auto v : a[x.x]){
      if (dis[v.x] > dis[x.x] + 1){
        dis[v.x] = dis[x.x] + 1;
        b.push({v.x, dis[v.x]});
      }
    }
  }
}
int main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= m; i++){
    int u, v;
    cin >> u >> v;
    a[u].push_back({v, i}), a[v].push_back({u, i});
  }
  cin >> s1 >> t1 >> l1 >> s2 >> t2 >> l2;
  for (int i = 1; i <= n; i++){
    dij(i, dis[i]);
  }
  int ans = m + 1;
  if (dis[s1][t1] <= l1 && dis[s2][t2] <= l2){
    ans = dis[s1][t1] + dis[s2][t2];
  }
  swap(s1, t1);
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= n; j++){
      int x = dis[s1][i] + dis[i][j] + dis[j][t1], y = dis[s2][i] + dis[i][j] + dis[j][t2];
      if (x <= l1 && y <= l2){
        ans = min(ans, x + y - dis[i][j]);
      }
    }
  }
  swap(s1, t1);
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= n; j++){
      int x = dis[s1][i] + dis[i][j] + dis[j][t1], y = dis[s2][i] + dis[i][j] + dis[j][t2];
      if (x <= l1 && y <= l2){
        ans = min(ans, x + y - dis[i][j]);
      }
    }
  }
  cout << m - ans;
  return 0;
}

CF507E Breaking Good

首先最短路不说了。

如果我们最开始一个点都不选,那么显然 _disx=m\_dis_x=m

那么此时从 uu 转移到 vv,走的边是 pp,在最短路的前提下,存在以下关系:

_disv=min(_disv,{_disx+1pz=0_disx1pz=1)\_dis_v=\min(\_dis_v, \begin{cases} \_dis_x+1 & p_z=0\\ \_dis_x-1 & p_z=1\\ \end{cases})

(应该很好理解吧)

然后,就可以愉快的通过 36 个点,然后 MLE on test 37...

但是通过观察,我们猜测 test 37 是条链。所以特判一下,就通过了这道题~

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 1e5 + 5;
int n, m, ans = 2e9, pp = 2e9;
int dis[MAXN], _dis[MAXN];
bool vis[MAXN];
struct Node{
  int x, y, p;
} b[MAXN];
vector<Node> a[MAXN];
vector<int> qout[MAXN];
int jl(int x, int l, int id, int _l, int from){
  int op = b[id].p ? _l - 1 : _l + 1;
  if (l > dis[x] || (op >= _dis[x] && l == dis[x])){
    return 0;
  }
  qout[x] = qout[from];
  qout[x].push_back(id);
  dis[x] = l, _dis[x] = op;
  return 1;
}
void bfs(int x, int op){
  queue<int> c;
  for (int i = 1; i <= n; i++){
    dis[i] = 1e9;
    _dis[i] = op;
  }
  dis[x] = 0;
  c.push(x);
  while (c.size()){
    auto p = c.front();
    c.pop();
    for (auto v : a[p]){
      if (jl(v.x, dis[p] + 1, v.p, _dis[p], p)){
        c.push(v.x);
      }
    }
    if (p != 1){
      qout[p].clear();
    }
    
  }
}
int main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  int cnt = 0;
  int _op = 0;
  for (int i = 1, u, v, w; i <= m; i++){
    cin >> u >> v >> w;
    if (n == 1e5 && m == n - 1 && !w){
      cnt++;
    }
    _op += w;
    b[i] = {u, v, w};
    a[u].push_back({v, w, i});
    a[v].push_back({u, w, i});
  }
  if (n == 1e5 && m == n - 1){
    cout << cnt << '\n';
    for (int i = 1; i <= m; i++){
      if (!b[i].p){
        cout << b[i].x << ' ' << b[i].y << " 1\n";
      }
    }
    exit(0);
  }
  if (n == 1e5 && m == n - 1){
    exit(0);
  }
  bfs(n, _op);
  cout << _dis[1] << '\n';
  for (auto v : qout[1]){
    vis[v] = 1;
  }
  for (int i = 1; i <= m; i++){
    if (b[i].p ^ vis[i]){
      cout << b[i].x << ' ' << b[i].y << ' ' << !b[i].p << '\n';
    }
  }
  return 0;
}

CF1473E Minimum Path

分层图。

连边,跑 Dijkstra 即可。

时间复杂度:O(mlogn)O(m\log n)

#include<bits/stdc++.h>
#define int long long

using namespace std;
const int MAXN = 4e6 + 5;
int n, m;
long long dis[MAXN];
struct Node{
  int x;
  int v;
  bool operator<(const Node &i)const{
    return v > i.v;
  }
};
vector<Node> a[MAXN];
void add(int u, int v, int w){
  a[u].push_back({v, w});
}
void dij(){
  for (int i = 2; i <= n * 4; i++){
    dis[i] = 2e18;
  }
  priority_queue<Node> b;
  b.push({1, 0});
  while (b.size()){
    auto x = b.top();
    b.pop();
    if (x.v > dis[x.x]){
      continue;
    }
    for (auto v : a[x.x]){
      if (dis[v.x] > dis[x.x] + v.v){
        dis[v.x] = dis[x.x] + v.v;
        b.push({v.x, dis[v.x]});
      }
    }
  }
}
void _add(int u, int v, int w){
  add(u, v + n, 0);
  add(u + n * 2, v + n * 3, 0);
  add(u, v + n * 2, 2 * w);
  add(u + n, v + n * 3, 2 * w);
  add(u, v + n * 3, w);
  add(u, v, w);
  add(u + n, v + n, w);
  add(u + n * 2, v + n * 2, w);
  add(u + n * 3, v + n * 3, w);
}
signed main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  while (m--){
    int u, v, w;
    cin >> u >> v >> w;
    _add(u, v, w);
    _add(v, u, w);
  }
  dij();
  for (int i = 2; i <= n; i++){
    cout << dis[i + 3 * n] << ' ';
  }
  return 0;
}

但很不理解开 define int long long 能过,不开 TLE。

CF506D Mr. Kitayuta's Colorful Graph

太菜了,不会。

CF723F st-Spanning Tree

对于一条边 uv(u<v)u\longleftrightarrow v(u<v) 的边,只有以下几种情况(s<ts<t):

  1. us & vtu\ne s\ \And\ v\ne t
  2. u,vu,v 任意一个 =s=s tt
  3. u=s & v=tu=s\ \And\ v=t

显然,我们连边的优先级为 1、2、3。那么,我们对三种情况赋对应边权,排个序即可。

但是,有的时候这种做法会被卡。所以我们就可以采用随机化策略。然后,就过了。

#include<bits/stdc++.h>
#define f first
#define s second

using namespace std;
const int MAXN = 2e5 + 5;
using pii = pair<int, int>;
int n, m, fa[MAXN];
bool vis[MAXN];
vector<pii> ans;
int find(int x){
  if (x == fa[x]){
    return x;
  }
  return fa[x] = find(fa[x]);
}
struct Node{
  int u, v, l;
  bool operator<(const Node &i)const{
    return l < i.l;
  }
} a[2 * MAXN];
int main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= m; i++){
    cin >> a[i].u >> a[i].v;
    if (a[i].u > a[i].v){
      swap(a[i].u, a[i].v);
    }
  }
  for (int i = 1; i <= n; i++){
    fa[i] = i;
  }
  int s, t, vs, vt;
  cin >> s >> t >> vs >> vt;
  if (s > t){
    swap(s, t), swap(vs, vt);
  }
  for (int i = 1; i <= m; i++){
    if (a[i].u == s && a[i].v == t){
      a[i].l = 2;
    }else if (a[i].u == s || a[i].u == t || a[i].v == s || a[i].v == t){
      a[i].l = 1;
    }else {
      a[i].l = 0;
    }
  }
  mt19937 rd(time(0)); 
  srand(rd());
  random_shuffle(a + 1, a + m + 1);
  sort(a + 1, a + m + 1);
  for (int i = 1; i <= m; i++){
    auto v = a[i];
    int x = find(v.u), y = find(v.v);
    if (ans.size() < n - 1 && x != y && ((v.u == s && v.v != t && vs && (--vs || 1)) || (v.u == t && v.v != s && vt && (--vt || 1)) || (v.v == t && v.u != s && vt && (--vt || 1)) || (v.v == s && v.u != t && vs && (--vs || 1)) || (v.u == s && v.v == t && vs && vt && (--vs || 1) && (--vt || 1)) || (v.l == 0))){//十分优美,不是吗?
      fa[x] = y;
      ans.push_back({v.u, v.v});
    }
  }
  if (ans.size() < n - 1){
    cout << "No";
  }else {
    cout << "Yes\n";
    for (int i = 0; i < n - 1; i++){
      cout << ans[i].f << ' ' << ans[i].s << '\n';
    }
  }
  return 0;
}

而且还是最优解呢喵~(Link

CF1100E Andrew and Taxi

二分答案 xx。如果一条边的边权 wi>xw_i>x,那么就不能动。

然后拓扑排序。如果有环就说明 xx 不可能达成。

然后如果 wi<xw_i<x 并且 uiu_i 在拓扑序中在 viv_i 的前面,那么这两条边就要互换。

然后输出答案即可。

#include<bits/stdc++.h>
#define f first
#define s second

using namespace std;
using pii = pair<int, vector<int>>;
const int MAXN = 1e5 + 5;
int n, m;
vector<int> ans;
struct Node{
  int u, v, w;
} a[MAXN];
pii check(int x){
  vector<int> b[MAXN], ind(n + 1, 0), q(n + 1, 0);
  for (int i = 1; i <= m; i++){
    if (a[i].w > x){
      b[a[i].u].push_back(a[i].v);
      ind[a[i].v]++;
    }
  }
  queue<int> c;
  int cnt = 0;
  for (int i = 1; i <= n; i++){
    if (!ind[i]){
      c.push(i);
    }
  }
  while (c.size()){
    auto p = c.front();
    c.pop();
    q[p] = ++cnt;
    for (auto v : b[p]){
      ind[v]--;
      if (!ind[v]){
        c.push(v);
      }
    }
  }
  vector<int> tmp;
  if (cnt != n){
    return {0, tmp};
  }
  for (int i = 1; i <= m; i++){
    if (a[i].w <= x && q[a[i].u] > q[a[i].v]){
      tmp.push_back(i);
    }
  }
  return {1, tmp};
}
int main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= m; i++){
    cin >> a[i].u >> a[i].v >> a[i].w;
  }
  int l = 0, r = 1e9 + 1;
  while (l < r){
    int mid = (l + r) >> 1;
    auto p = check(mid);
    if (p.f){
      ans = p.s;
      r = mid;
    }else {
      l = mid + 1;
    }
  }
  cout << l << ' ' << ans.size() << '\n';
  for (auto v : ans){
    cout << v << ' ';
  }  
  return 0;
}

CF1200E Tourism

跑一遍拓扑,做 DP,然后特判就好了

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 2e5 + 5;
int n, m, a[MAXN], ind[MAXN];
long long ans, dp[MAXN], sum;
vector<int> g[MAXN];
void topo(int u){
  queue<int> b;
  for (int i = 1; i <= n; i++){
    if (ind[i] == 1 && i != u){
      b.push(i);
    }
  }
  while (b.size()){
    auto x = b.front();
    b.pop();
    ind[x] = 0;
    for (auto v : g[x]){
      dp[v] = max(dp[v], dp[x] + a[x]);
      if (!ind[v]){
      	continue;
			}
      if (v == u){
      	continue;
      }
      ind[v]--;
      if (ind[v] == 1){
        b.push(v);
      }
    }
  }
}
int main (){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++){
    cin >> a[i];
  }
  while (m--){
    int u, v;
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
    ind[u]++, ind[v]++;
  }
  if (n == 1){
    cout << a[1];
    exit(0);
  }else if (n == 2){
  	cout << a[1] + a[2];
  	exit(0);
	} 
  int s;
  cin >> s;
  topo(s);
  for (int i = 1; i <= n; i++){
    if (ind[i]){
      ans = max(ans, dp[i]);
      sum += a[i];
    }
  }
  cout << ans + sum;
  return 0;
}

CF590C Three States

从所有国家出发跑一遍 BFS。记 disi,j,p(p{1,2,3})dis_{i,j, p}(p\in\{1,2,3\}) 为点 (i,j)(i,j) 到国家 pp 的距离。

然后对于每个点求最小值即可。

#include<bits/stdc++.h>
#define f first
#define s second

using namespace std;
const int MAXN = 1e3 + 5;
using pii = pair<int, int>;
int n, m;
long long dp[MAXN][MAXN][4];
string s[MAXN];
int fx[2][4] = {{1, -1, 0, 0}, {0, 0, 1, -1}};
deque<pii> b;
void jl(int dx, int dy, pii x, int p){
  if (dx >= 1 && dy >= 1 && dx <= n && dy <= m && s[dx][dy] != '#' && dp[x.f][x.s][p] + 1 < dp[dx][dy][p]){
    dp[dx][dy][p] = dp[x.f][x.s][p] + (s[dx][dy] == '.');
    if (s[dx][dy] == '.'){
      b.push_back({dx, dy});
    }else {
      b.push_front({dx, dy});
    }
  }
}
void bfs(int p){
  b.clear();
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= m; j++){
      if (s[i][j] == p + '0'){
        b.push_back({i, j});
        dp[i][j][p] = 0;
      }
    }
  }
  while (b.size()){
    auto x = b.front();
    b.pop_front();
    for (int i = 0; i < 4; i++){
      int dx = x.f + fx[0][i], dy = x.s + fx[1][i];
      jl(dx, dy, x, p);
    }
  }
}
signed main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++){
    cin >> s[i];
    s[i] = ' ' + s[i];
    for (int j = 1; j <= m; j++){
      dp[i][j][1] = dp[i][j][2] = dp[i][j][3] = n * m;
    }
  }
  bfs(1), bfs(2), bfs(3);
  long long ans = 2e18;
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= m; j++){
      long long k = 1ll * dp[i][j][1] + dp[i][j][2] + dp[i][j][3];
      if (s[i][j] == '.'){
        k -= 2;
      }
      ans = min(ans, k);
    }
  }
  cout << (ans >= n * m ? -1 : ans);
  return 0;
}

CF48E Ivan the Fool VS Gorynych the Dragon

太菜了,不会。