2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17)

Contest Info


传送门

Solved A B C D E F G H I J K L
8 / 13 Ø O O Ø O - - - Ø Ø - Ø
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Archery Tournament

题意:
现在有一二维平面,之后有两种操作,一种操作是生成一个圆,圆心为\((x_i,y_i)\),其中半径为\(y_i\)。保证任意两个圆不含有相交部分;另一种操作是对于平面任意一个位置开枪,若击中点在圆内则这个圆消失,若消失了输出圆的编号。

思路:
我们对于每一个击中点,要找到覆盖他的圆。
假设击中点为\((x,y)\),比较直观可以发现只有左边第一个直径大于\(y\)以及右边第一个直径大于\(y\)的圆才能进行覆盖。所以我们线段树维护一下直径即可。
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/3 8:40:00
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;
 
int A[N];
struct node {
    int op, x, y;
}a[N];
int n;
 
void Hash(int* a) {
    sort(a + 1, a + a[0] + 1);
    a[0] = unique(a + 1, a + a[0] + 1) - a - 1;
}
int find(int* a, int x) {
    return lower_bound(a + 1, a + a[0] + 1, x) - a;
}
 
pii maxv[N << 2];
 
void push_up(int o) {
    if (maxv[o << 1] > maxv[o << 1|1]) {
        maxv[o] = maxv[o << 1];
    } else {
        maxv[o] = maxv[o << 1|1];
    }
}
 
void update(int o, int l, int r, int x, int v, int id) {
    if (l == r) {
        maxv[o] = MP(v, id);
        return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) update(o << 1, l, mid, x, v, id);
    else update(o << 1|1, mid + 1, r, x, v, id);
    push_up(o);
}
 
pii queryL(int o, int l, int r, int L, int R, int v) {
    if (L > R) return MP(-1, -1);
    if (l == r) {
        return maxv[o];
    }
    int mid = (l + r) >> 1;
    pii res = MP(-1, -1);
    if (R > mid && maxv[o << 1|1].fi > v) {
        res = queryL(o << 1|1, mid + 1, r, L, R, v);
    }
    if (L <= mid && res.fi <= v && maxv[o << 1].fi > v) {
        res = queryL(o << 1, l, mid, L, R, v);
    }
    return res;
}
 
pii queryR(int o, int l, int r, int L, int R, int v) {
    if (L > R) return MP(-1, -1);
    if (l == r) {
        return maxv[o];
    }
    int mid = (l + r) >> 1;
    pii res = MP(-1, -1);
    if (L <= mid && maxv[o << 1].fi > v) {
        res = queryR(o << 1, l, mid, L, R, v);
    }
    if (R > mid && res.fi <= v && maxv[o << 1|1].fi > v) {
        res = queryR(o << 1|1, mid + 1, r, L, R, v);
    }
    return res;
}
 
void run() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int op, x, y; cin >> op >> x >> y;
        a[i] = node {op, x, y};
        A[++A[0]] = x;
    }
    Hash(A);
    auto shoot = [&] (int id, int x, int y) {
        return 1ll * (x - a[id].x) * (x - a[id].x) + 1ll * (y - a[id].y) * (y - a[id].y) < 1ll * a[id].y * a[id].y;
    };
    for (int i = 1; i <= n; i++) {
        int op = a[i].op, x = a[i].x, y = a[i].y;
        if (op == 1) {
            update(1, 1, n, find(A, x), 2 * y, i);
        } else {
            int p = find(A, x);
            pii p1 = queryL(1, 1, n, 1, p, y), p2 = queryR(1, 1, n, p + 1, n, y);
            if (p1.fi > y && shoot(p1.se, x, y)) {
                update(1, 1, n, find(A, a[p1.se].x), 0, 0);
                cout << p1.se << '\n';
                continue;
            }
            if (p2.fi > y && shoot(p2.se, x, y)) {
                update(1, 1, n, find(A, a[p2.se].x), 0, 0);
                cout << p2.se << '\n';
                continue;
            }
            cout << -1 << '\n';
        }
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

题解的做法是显然左右两边至多\(log\)个圆能覆盖到\(x=x_0\)这条线上,那么我们类似于标记可持久化那样,每个线段树结点维护一个\(set\)\(set\)中的结点表示覆盖当前整个区间的圆。
加入一个圆时直接将\([x_i-y_i,x_i+y_i]\)插入到线段树中即可。
时间复杂度为\(O(nlog^2n)\)

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/3 12:37:31
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
 
int A[N];
struct node {
    int op, x, y;
}a[N];
int n;
 
void Hash(int* a) {
    sort(a + 1, a + a[0] + 1);
    a[0] = unique(a + 1, a + a[0] + 1) - a - 1;
}
int find(int x) {
    return lower_bound(A + 1, A + A[0] + 1, x) - A;
}
 
set <int> tr[N << 2];
auto shoot = [&] (int id, int x, int y) {
    return 1ll * (x - a[id].x) * (x - a[id].x) + 1ll * (y - a[id].y) * (y - a[id].y) < 1ll * a[id].y * a[id].y;
};
 
int ans;
 
void insert(int o, int l, int r, int L, int R, int v) {
    if (L <= l && r <= R) {
        if (v > 0) {
            tr[o].insert(v);
        } else {
            tr[o].erase(-v);
        }
        return;
    }
    int mid = (l + r) >> 1;
    if (L <= mid) insert(o << 1, l, mid, L, R, v);
    if (R > mid) insert(o << 1|1, mid + 1, r, L, R, v);
}
 
void query(int o, int l, int r, int p, int x, int y) {
    for (auto it : tr[o]) {
        if (shoot(it, x, y)) {
            ans = it;
        }
    }
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (p <= mid) query(o << 1, l, mid, p, x, y);
    else query(o << 1|1, mid + 1, r, p, x, y);
}
 
void run() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int op, x, y; cin >> op >> x >> y;
        a[i] = node {op, x, y};
        if (op == 1) {
            A[++A[0]] = x;
        } else {
            A[++A[0]] = x - y;
            A[++A[0]] = x + y;
        }
    }
    Hash(A);   
    for (int i = 1; i <= n; i++) {
        int op = a[i].op, x = a[i].x, y = a[i].y;
        if (op == 1) {
            insert(1, 1, A[0], find(x - y), find(x + y), i);
        } else {
            ans = -1;
            query(1, 1, A[0], find(x), x, y);
            cout << ans << '\n';
            if (ans != -1) {
                insert(1, 1, A[0], find(a[ans].x - a[ans].y), find(a[ans].x + a[ans].y), -ans);
            }
        }   
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

B. Box

只有几种情况,分别判断一下就行。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/2 13:27:05
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
int w, h;
 
void ok(int x, int y) {
    if (x > y) swap(x, y);   
    if (x <= w && y <= h) {
        cout << "Yes" << '\n';
        exit(0);
    }
}
 
void run() {
    int a[3];
    for (int i = 0; i < 3; i++) {
        cin >> a[i];
    }
    cin >> w >> h;
    if (w > h) swap(w, h);
    sort(a, a + 3);
    do {
        ok(2 * a[2] + a[0], 2 * a[2] + 2 * a[1]);
        ok(3 * a[0] + a[1] + a[2], a[1] + a[2]);
        ok(2 * a[2] + 2 * a[1], a[0] + a[1] + a[2]);
        ok(2 * a[0] + a[1] + a[2], a[0] + a[1] + a[2]);
    } while (next_permutation(a, a + 3));
    cout << "No" << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

C. Connections

题意:
给出一个大于\(2n\)条边的有向强连通图,现在要保留\(2n\)条边,使得其仍为一个强连通图。

思路:
正反图从同一个结点分别dfs一次就行。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/2 15:11:18
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
int n, m;
vector <pii> G[N];
bool chk[N], vis[N];
 
void dfs(int u) {
    vis[u] = true;
    for (auto it : G[u]) {
        int v = it.fi, id = it.se;
        if (!vis[v]) {
            chk[id] = true;
            dfs(v);   
        }
    }   
}
 
void run() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        G[i].clear();
        vis[i] = false;
    }
    for (int i = 1; i <= m; i++) {
        chk[i] = false;
    }
    vector <pii> edges;
    edges.push_back(MP(0, 0));
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(MP(v, i));
        edges.push_back(MP(u, v));
    }
    dfs(1);
    for (int i = 1; i <= n; i++) {
        G[i].clear();
        vis[i] = false;
    }
    for (int i = 1; i <= m; i++) {
        int u = edges[i].fi, v = edges[i].se;
        G[v].push_back(MP(u, i));
    }
    dfs(1);
    int cnt = 0;
    for (int i = 1; i <= m; i++) {
        if (!chk[i]) {
            ++cnt;
            cout << edges[i].fi << ' ' << edges[i].se << '\n';
        }
        if (cnt == m - 2 * n) break;
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

D. Designing the Toy

在一个面上先斜着构造,然后填补剩下的即可。
代码还是挺有意思的,我们不考虑平面的变化,直接翻转坐标轴,所以可以在固定平面大小的情况下求出构造方案,之后变换坐标轴即可。
详见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/2 20:15:44
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 100 + 5;
 
pii a[3];
int mp[N][N];
 
void run() {
    for (int i = 0; i < 3; i++) {
        cin >> a[i].fi;
        a[i].se = i;
    }
    sort(a, a + 3);
    if (a[0].fi * a[1].fi < a[2].fi) {
        cout << -1 << '\n';
        return;
    }
    for (int i = 0; i < a[0].fi; i++) {
        mp[i][i] = 1;
    }
    int d = a[1].fi - a[0].fi;
    for (int i = 0; i < d; i++) {
        mp[a[0].fi + i][a[0].fi - 1] = 1;
    }
    d = a[2].fi - a[1].fi;
    for (int i = 0; i < a[1].fi && d; i++) {
        for (int j = 0; j < a[0].fi && d; j++) {
            if (mp[i][j]) continue;
            mp[i][j] = 1;   
            --d;
        }
    }
    cout << a[2].fi << '\n';
    if (a[0].se == 1 && a[1].se == 0) {
        for (int y = 0; y <= 100; y++) {
            for (int z = 0; z <= 100; z++) {
                if (mp[y][z]) {
                    cout << 0 << ' ' << y << ' ' << z << '\n';
                }
            }
        }
    }
    if (a[0].se == 0 && a[1].se == 1) {
        for (int y = 0; y <= 100; y++) {
            for (int z = 0; z <= 100; z++) {
                if (mp[z][y]) {
                    cout << 0 << ' ' << y << ' ' << z << '\n';
                }
            }
        }       
    }
    if (a[0].se == 1 && a[1].se == 2) {
        for (int x = 0; x <= 100; x++) {
            for (int y = 0; y <= 100; y++) {
                if (mp[y][x]) {
                    cout << x << ' ' << y << ' ' << 0 << '\n';
                }
            }
        }
    }
    if (a[0].se == 2 && a[1].se == 1) {
        for (int x = 0; x <= 100; x++) {
            for (int y = 0; y <= 100; y++) {
                if (mp[x][y]) {
                    cout << x << ' ' << y << ' ' << 0 << '\n';
                }
            }
        }       
    }
    if (a[0].se == 0 && a[1].se == 2) {
        for (int x = 0; x <= 100; x++) {
            for (int z = 0; z <= 100; z++) {
                if (mp[z][x]) {
                    cout << x << ' ' << 0 << ' ' << z << '\n';
                }
            }
        }       
    }
    if (a[0].se == 2 && a[1].se == 0) {
        for (int x = 0; x <= 100; x++) {
            for (int z = 0; z <= 100; z++) {
                if (mp[x][z]) {
                    cout << x << ' ' << 0 << ' ' << z << '\n';
                }
            }
        }
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

E. Easy Quest

签到。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/2 13:05:44
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
void run() {
    int n; cin >> n;
    vector <int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector <int> cnt(1001);
    int all = 0;
    vector <int> ans;
    for (int i = 0; i < n; i++) {
        if (a[i] == 0) {
            ++all;
        }
        if (a[i] > 0) {
            ++cnt[a[i]];
        }
        if (a[i] < 0) {
            if (cnt[-a[i]] > 0) {
                --cnt[-a[i]];
            } else if (all > 0) {
                ans.push_back(-a[i]);
                --all;
            } else {
                cout << "No" << '\n';
                return;
            }
        }
    }
    while (all--) {
        ans.push_back(1);
    }
    cout << "Yes" << '\n';
    for (auto it : ans) cout << it << ' ';
    cout << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

I. Interactive Sor

题意:
这是一个交互题。
现在随机给定一个\(1\)~\(n,n\leq 10^4\)的排列,并且依次将偶数、奇数分别放入两个数组\(e,o\)
现在最多询问\(300000\)次,每次询问两个数\(i,j\),之后会返回\(e[i],o[j]\)大小关系。
最后要输出\(e,o\)两个数组。

思路:
考虑一下询问过程,我们任选一个\(i,j\),那么我们能将奇数划分为两部分,并且知道\(e[i]\)的值。
这有点类似于快速排序的过程,我们知道快速排序的时间复杂度一般为\(O(nlogn)\)
所以我们就直接这样模拟,不断划分\(o\)数组,并且同时能够确定\(e\)数组就行,模拟的时间复杂度为\(O(n^2)\)
因为数据随机,时间复杂度为\(O(nlogn)\)
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/4 13:43:21
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e4 + 5;
 
string query(int i, int j) {
    cout << "?" << ' ' << i << ' ' << j << endl;
    string s; cin >> s;
    return s;
}
 
bool chk(int i, int j) {
    return query(i, j) == "<";
}
 
int n;
vector <int> P[N];
vector <int> Less, More;
 
bool solve(int i, int x) {
    Less.clear(), More.clear();
    for (auto it : P[x]) {
        if (chk(i, it)) More.push_back(it);
        else Less.push_back(it);
    }
    return sz(Less) && sz(More);
}
 
int ans0[N], ans1[N];
 
void run() {
    cin >> n;
    int m0 = n / 2, m1 = n - n / 2;
    for (int i = 1; i <= m1; i++) {
        P[1].push_back(i);
    }
    int tot = 1;
    for (int i = 1; i <= m0; i++) {
        int l = 1, r = tot + 1, mid;
        while (l < r) {
            mid = (l + r) >> 1;
            if (chk(i, P[mid][0])) r = mid;
            else l = mid + 1;
        }
        int t = 0;
        for (int j = l; j >= max(1, l - 1); j--) {
            if (solve(i, j)) {
                for (int k = ++tot; k > j + 1; k--) {
                    P[k] = P[k - 1];
                }
                t = j;
                P[j] = Less;
                P[j + 1] = More;
                break;
            }
        }
        if (t == 0) {
        	ans0[i] = m0;
        	continue;
		}
        for (int j = 1; j <= t; j++) {
            ans0[i] += sz(P[j]);
        }
    }
    for (int i = 1; i <= m1; i++) {
        ans1[P[i][0]] = 2 * i - 1;
    }
    cout << "!";
    for (int i = 1; i <= m0; i++) {
        ans0[i] *= 2;
        cout << ' ' << ans0[i];
    }
    for (int i = 1; i <= m1; i++) {
        cout << ' ' << ans1[i];
    }
    cout << endl;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

J. Journey from Petersburg to Moscow

题意:
给定一个\(n\)个点\(m\)条边的带权无向图。
若现在有一条\(1\rightarrow n\)的路径,从大到小依次为\(c_1,c_2,\cdots,c_t\),那么这条路的权值为前\(k\)大的边权之和。
现在问权值最短的路径权值为多少。
\(1\leq n,m\leq 3000,k\leq n\)

思路:
考虑\(1\rightarrow k\)的最长路径长度不超过\(k\),那么答案显然为最短路径。
接下来考虑其余的情况,显然对于每条路径寻找前\(k\)大边权不现实,接下来巧妙的就是,我们枚举第\(k\)大的边权为\(x\),那么令\(w'(e)=max(0,w(e)-k)\),之后再跑最短路,最后将答案加上\(kx\)即可。
容易发现这样做正确性是有保证的,我们分两种情况考虑:

  • 枚举的边权\(x\)小于第\(k\)大边权,显然最后加上\(kx\)过后会多出来一部分;
  • 枚举的边权\(x\)大于第\(k\)大边权,最后前\(k\)大边边权和都会多一部分。

所以这其实是一个单峰函数,刚好\(x=k\)时取得极小值。
那么我们直接枚举边权,在所有情况中取最小的就是答案。
很巧妙!单峰函数这一点并不是很好发现。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/4 16:37:19
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 3000 + 5;
 
int n, m, k;
 
struct Edge{
    int v, w, next;   
}e[N << 1];
ll dis[N];
struct Dijkstra{
    struct node{
        ll d, u;
        bool operator < (const node &A) const {
            return d > A.d;
        }   
    };
    int head[N], tot;
    bool vis[N];
    void init() {
        memset(head, -1, sizeof(head)); tot = 0;   
    }
    void adde(int u, int v, int w) {
        e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;   
    }
    void dij(int s) {
        priority_queue <node> q;
        memset(dis, INF, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        dis[s] = 0;
        q.push(node{0, s});
        while(!q.empty()) {
            node cur = q.top(); q.pop();
            int u = cur.u, d = cur.d;
            if(vis[u]) continue;
            vis[u] = 1;
            for(int i = head[u]; i != -1; i = e[i].next) {
                int v = e[i].v;
                if(dis[v] > dis[u] + e[i].w) {
                    dis[v] = dis[u] + e[i].w;
                    q.push(node{dis[v], v});   
                }
            }   
        }
    }
}solver;
 
void run() {
    cin >> n >> m >> k;
    vector <pair<pii, int>> edges(m + 1);
    solver.init();
    for (int i = 1; i <= m; i++) {
        int u, v, w; cin >> u >> v >> w;
        edges[i] = MP(MP(u, v), w);
        solver.adde(u, v, w);
        solver.adde(v, u, w);
    }
    solver.dij(1);
    ll ans = dis[n];
    for (int j = 1; j <= m; j++) {
        solver.init();
        int x = edges[j].se;
        for (int i = 1; i <= m; i++) {
            auto it = edges[i];
            int u = it.fi.fi, v = it.fi.se, w = it.se;
            solver.adde(u, v, max(w - x, 0));
            solver.adde(v, u, max(w - x, 0));
        }
        solver.dij(1);
        ans = min(ans, dis[n] + 1ll * k * x);
    }
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

L. Laminar Family

题意:
给定一颗树以及若干条简单路径。
现在问是否存在两条路径他们包含重复的结点并且结点集合不为包含关系。
最后输出yes/no。

思路:
我们留下所有的简单路径,并且删除其余不需要的边,发现若存在度数\(>2\)的点肯定存在。
排除掉上述情况过后只会剩下若干条链,那么接下来就是一个括号匹配问题,我们只需要给所有的区间排个序,剩下部分即是一个非常经典的模型了。
当然也可以直接用线段树区间赋值、并且维护区间最小/最大值来做,此时我们要按照长度排序,就不赘述了。
还是有点细节,具体见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/6/3 16:43:39
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
void sayNo() {
    cout << "No" << '\n';
    exit(0);   
}
 
int n, m;
vector <int> G[N];
 
int f[N][20], deep[N];
void dfs(int u, int fa) {
    deep[u] = deep[fa] + 1;
    f[u][0] = fa;
    for(int i = 1; i < 20; i++) {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }   
    for(auto v : G[u]) if(v != fa) {
        dfs(v, u);
    }
}
int LCA(int x, int y) {
    if(deep[x] < deep[y]) swap(x, y);
    for(int i = 19; i >= 0; i--) {
        if(deep[f[x][i]] >= deep[y]) x = f[x][i];
    }  
    if(x == y) return x;
    for(int i = 19; i >= 0; i--) {
        if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];  
    }
    return f[x][0];
}
 
int d[N];
pii path[N];
 
vector <int> nG[N];
int deg[N];
 
void dfs2(int u, int fa) {
    for (auto v : G[u]) {
        if (v != fa) {
            dfs2(v, u);
            d[u] += d[v];
        }
    }
    if (fa && d[u]) {
        nG[fa].push_back(u);
        nG[u].push_back(fa);
        ++deg[fa], ++deg[u];
    }
}
 
bool vis[N];
int dfn[N], T;
 
vector <int> a;
void dfs3(int u, int fa) {
    vis[u] = true;
    dfn[u] = ++T;
    a.push_back(u);
    for (auto v : nG[u]) {
        if (v != fa) {
            dfs3(v, u);
        }
    }
}
 
vector <pii> vl[N], vr[N];
void solve() {
    vector <int> sta;
    for (auto it : a) {
        for (auto pairs : vl[it]) {
            sta.push_back(pairs.fi);
        }
        for (auto pairs : vr[it]) {
            if (sz(sta) && sta.back() == pairs.fi) {
                sta.pop_back();
            } else {
                sayNo();
            }
        }
    }
}
 
void run() {
    cin >> n >> m;
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        path[i] = MP(u, v);
        ++d[u], ++d[v], d[LCA(u, v)] -= 2;
    }
    dfs2(1, 0);
    for (int i = 1; i <= n; i++) {
        if (deg[i] > 2) {
            sayNo();
        }
    }
    for (int i = 1; i <= n; i++) {
        if (!vis[i] && deg[i] == 1) {
            a.clear();
            dfs3(i, 0);
        }
    }
    for (int i = 1; i <= m; i++) {
        if (dfn[path[i].fi] > dfn[path[i].se]) {
            swap(path[i].fi, path[i].se);
        }        
    }
    for (int i = 1; i <= m; i++) {
        int u = path[i].fi, v = path[i].se;
        if (u == v) continue;
        vl[u].push_back(MP(i, dfn[v]));
        vr[v].push_back(MP(i, dfn[u]));
    }
    for (int i = 1; i <= n; i++) {
        if (sz(vl[i]) && sz(vr[i])) {
            sayNo();
        }
        sort(all(vl[i]), [&] (pii A, pii B) {
            if (A.se != B.se) return A.se > B.se;
            return A.fi > B.fi;
        });
        sort(all(vr[i]), [&] (pii A, pii B) {
            if (A.se != B.se) return A.se > B.se;
            return A.fi < B.fi;
        });
    }
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n; i++) {
        if (!vis[i] && deg[i] == 1) {
            a.clear();
            dfs3(i, 0);
            solve();
        }
    }
    cout << "Yes" << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
posted @ 2020-06-06 20:07  heyuhhh  阅读(262)  评论(0编辑  收藏  举报