牛客练习赛61

传送门

A.打怪

签到。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/10 19:00:16
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#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 h, a, H, A; cin >> h >> a >> H >> A;
    if(A == 0) {
        if(a > 0) cout << -1 << '\n';
        else cout << 0 << '\n';
        return;
    }
    int x = 0;
    int th = H;
    while(h > 0) {
        H -= a;
        if(H <= 0) {
            ++x;
            H = th;
        } else h -= A;
    }
    cout << x << '\n';
}

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;
}

B.吃水果

不妨设\(n<m\),然后贪心即可,具体贪心方式如下:

  • 倍增\(n\),使得\(2*n>m\)
  • \(t=2*n-m\),那么\(2*(n-t)=m-t\),所以\(n-=t,m-=t\)
  • 之后再令\(n\)倍增使得\(n=m\)
  • 最后同时减到\(0\)即可。

贪心的思路大致是用最少的次数使得\(n=m\),那么\(t=2n-m\),之后我们通过一次倍增就可以使得\(n=m\)。所以要保证\(0\leq t\)并且\(t\)尽量小,我们一开始贪心倍增即可。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/10 19:10:20
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#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, m; cin >> n >> m;
    if(n > m) swap(n, m);
    int t = 0;
    while(n << 1 <= m) n <<= 1, ++t;
    int x = 2 * n - m;
    n -= x, m -= x, t += x;
    if(n != m) ++t;
    t += m;
    cout << t << '\n';
}

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;
}

C.四个选项

并查集+dp。
并查集合并后就是简单的背包\(dp\)

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/10 19:16:57
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#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 = 13;

ll dp[N][N][N][N][N];
int f[N];
int m, na, nb, nc, nd;

int find(int x) {return f[x] == x ? f[x] : f[x] = find(f[x]);}

void Union(int x, int y) {
    int fx = find(x), fy = find(y);
    if(fx != fy) f[fx] = fy;   
}

int s[N], tot;
int mp[N];

void add(ll& x, ll y) {x += y;}

void run() {
    cin >> na >> nb >> nc >> nd >> m;
    for(int i = 1; i <= 12; i++) f[i] = i;
    for(int i = 1; i <= m; i++) {
        int x, y; cin >> x >> y;
        Union(x, y);   
    }
    for(int i = 1; i <= 12; i++) {
        int fa = find(i);
        if(!mp[fa]) mp[fa] = ++tot;
        ++s[mp[fa]];
    }
    dp[0][0][0][0][0] = 1;
    for(int i = 0; i < tot; i++) {
        for(int a = 0; a <= na; a++) {
            for(int b = 0; b <= nb; b++) {
                for(int c = 0; c <= nc; c++) {
                    for(int d = 0; d <= nd; d++) {
                        int t = s[i + 1];
                        if(a + t <= na) add(dp[i + 1][a + t][b][c][d], dp[i][a][b][c][d]);
                        if(b + t <= nb) add(dp[i + 1][a][b + t][c][d], dp[i][a][b][c][d]);
                        if(c + t <= nc) add(dp[i + 1][a][b][c + t][d], dp[i][a][b][c][d]);
                        if(d + t <= nd) add(dp[i + 1][a][b][c][d + t], dp[i][a][b][c][d]);
                    }
                }   
            }
        }   
    }
    cout << dp[tot][na][nb][nc][nd] << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

D.最短路变短了

题意:
给定\(n\)个点\(m\)条边的有向带权图。
现在存在\(q\)个询问,每个询问将会翻转一条边,每次询问是独立的,回答是否当前最短路会变化。

思路:
注意到翻转一条边\((u,v)\)只会影响经过\((u,v)\)这条边的最短路径,所以我们只需要检查\(s\rightarrow v\rightarrow u\rightarrow t\)的路径长度即可。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/10 19:29:43
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#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, M = 2e5 + 5;

int n, m;
struct Edge{
    int v, w, next;   
}e[M << 1];
struct EDGE {
    int u, v, w;   
}E[M];
ll dis[N], rdis[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;
    solver.init();
    for(int i = 1; i <= m; i++) {
        int u, v, w; cin >> u >> v >> w;
        solver.adde(u, v, w);
        E[i] = EDGE{u, v, w};
    }
    solver.dij(1);
    for(int i = 1; i <= n; i++) rdis[i] = dis[i];
    solver.init();
    for(int i = 1; i <= m; i++) {
        int u = E[i].u, v = E[i].v, w = E[i].w;
        solver.adde(v, u, w);   
    }
    solver.dij(n);
    for(int i = 1; i <= n; i++) swap(dis[i], rdis[i]);
    int q; cin >> q;
    while(q--) {
        int id; cin >> id;
        int u = E[id].u, v = E[id].v, w = E[id].w;
        ll now = dis[v] + rdis[u] + w;
        if(now < dis[n]) cout << "YES" << '\n';
        else cout << "NO" << '\n';   
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

E.相似的子串

题意:
给定一个字符串,要求取出\(k\)个位置不相交的子串,且他们之间任意两个的最长公共前缀的长度均不小于\(x\)。现在给出\(k\),求最大的\(x\)

题意:
这个题用hash可以轻松解决,我们只需要扫一遍,用数组记录该hash值上一次出现的位置即可。如果不相交,那么计数器加\(1\);否则就continue。该方法时间复杂度为\(O(nlogn)\)
比赛中我用的是后缀数组,后缀排序后,我们按照\(lcp\geq k\)将后缀分为若干个连续的区间(容易证明这些区间不会相交)。那么最多只会有\(n\)个位置,我们插入\(set\)之后直接暴力lower_bound即可。
因为位置至多\(O(n)\)个,所以时间复杂度为\(O(nlogn)\)
算上二分的复杂度的话时间复杂度即为\(O(nlog^2n)\)
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/10 20:27:31
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#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 n, k;
char s[N];
struct SA{                                       //sa:1...n  Rank:0...n-1
    int x[N], y[N], sa[N], c[N], height[N], Rank[N];
    int f[N][20], lg[N];
    int n;                                          //length
    void da(char *s, int m){
        n++;
        for(int i = 0; i < m; i++) c[i] = 0;
        for(int i = 0; i < n; i++) c[x[i] = s[i]]++;
        for(int i = 1; i < m; i++) c[i] += c[i - 1] ;
        for(int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
        for(int k = 1; k <= n; k <<= 1) {
            int p = 0 ;
            for(int i = n - k; i < n; i++) y[p++] = i ;
            for(int i = 0; i < n; i++) if(sa[i] >= k) y[p++] =sa[i] - k;
            for(int i = 0; i < m; i++) c[i] = 0;
            for(int i = 0; i < n; i++) c[x[y[i]]]++;
            for(int i = 1; i < m; i++) c[i] += c[i - 1];
            for(int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i] ;
            swap(x , y); p = 1; x[sa[0]] = 0;
            for(int i = 1; i < n; i++)
                x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p - 1 : p++;
            if(p >= n) break ;
            m = p;
        }
        n--;
        int k = 0;
        for(int i = 0; i <= n; i++) Rank[sa[i]] = i;
        for(int i = 0; i < n; i++) {
            if(k) k--;
            int j = sa[Rank[i] - 1];
            while(s[i + k] == s[j + k]) k++;
            height[Rank[i]] = k;
        }
    }
    ll count() {
        ll ans = 0;
        for(int i = 1; i <= n; i++) ans += n - sa[i] - height[i];
        return ans;
    }
    void init() {
        for(int i = 2; i < N; i++) lg[i] = lg[i >> 1] + 1;
        for(int i = 2; i <= n; i++) f[i][0] = height[i];
        for(int j = 1; j < 20; j++)
            for(int i = 2; i + (1 << j) - 1 <= n; i++)
                f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]) ;
    }
    int get_lcp(int l, int r) {
        ++l; if(l > r) return n + 1;
        int k = lg[r - l + 1];
        return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
}suf;

bool chk(int x) {
    for(int l = 1, r; l <= n; l = r + 1) {
        r = l;
        while(suf.get_lcp(l, r + 1) >= x) ++r;
        set <int> S;
        for(int i = l; i <= r; i++) S.insert(suf.sa[i]);
        auto now = *S.begin();
        int cnt = 1;
        while(1) {
            auto nxt = S.lower_bound(now + x);   
            if(nxt == S.end()) break;
            now = *nxt;
            ++cnt;
        }
        if(cnt >= k) return true;
    }
    return false;
}

void run() {
    cin >> n >> k;
    cin >> s;
    suf.n = n;
    suf.da(s, 520);
    suf.init();
    int l = 1, r = n / k + 3, mid;
    while(l < r) {
        mid = (l + r) >> 1;
        if(chk(mid)) l = mid + 1;
        else r = mid;   
    }
    cout << l - 1 << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

F.苹果树

题意:
给定一颗树,每个点一开始有一个权值\(a_i\)。之后会执行\(q\)次操作,每次操作可以选择一项执行:

  • \(1\ u\ x:\)结点\(u\)新增权值\(x\)
  • \(2\ u\ x\ y:\)询问从\(u\)出发到达权值在\([x,y]\)内的结点的最短距离。

思路:

  • 可以先考虑暴力的做法:考虑点分治,那么就相当于枚举每个点作为中转,之后在当前连通块内求经过该点的答案。
  • 求答案的话可以使用线段树解决,结点记录权值范围在\([l,r]\)区间的最小距离。那么按照时间序枚举当前连通块的所有更新和所有询问然后进行查询即可。
  • 据此即可得到一个离线算法,每个询问至多会被枚举\(O(logn)\)次,因为更新数据和询问答案时间复杂度都为\(O(logn)\),所以总时间复杂度为\(O(qlogn+nlog^2n)\)

这个题还有另外一种做法,因为带修改的点分治我们每次都会进行点分治一次,但每次选择的重心不会改变。那么我们考虑根据每次选择的重心建立点分树。
点分树有几个比较好的性质:

  • 树高为\(O(logn)\)级别,这个根据点分治的过程即可发现;
  • 所有经过结点\(u\)的最优路径在点分治过程中以\(u,fa[u],fa[fa[u]]..\)为重心时产生。这一点根据点分治过程也容易发现。

因为我们点分治过程枚举的是中间点,路径的另一半信息我们不知道,所以我们对每个结点维护子树上面的信息,那么根据\(u->fa[u]\)\(fa[u]\)子树上面的信息我们可以方便维护以\(fa[u]\)为重心时经过\(u\)点的答案。这一过程通过模拟点分治的过程也容易理解。
那么我们对每个结点建立动态开点的线段树,结点信息同第一种方法。
因为维护和查询的都是关于子树的信息,树高不超过\(O(logn)\),所以我们在修改和查询的时候暴力往上跳即可。
时间复杂度为\(O(nlog^2n)\)
第二种方法代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/16 16:22:33
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#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, MAX = 1e7 + 5;

int n, m;
vector <pii> G[N];
int tsz, rt;
int sz[N], Max[N], father[N];
bool vis[N];
void getrt(int u, int fa) {
    sz[u] = 1; Max[u] = 0;
    for(auto it : G[u]) {
        int v = it.fi, w = it.se;
        if(v == fa || vis[v]) continue;
        getrt(v, u);
        sz[u] += sz[v];
        if(sz[v] > Max[u]) Max[u] = sz[v];
    }
    Max[u] = max(Max[u], tsz - sz[u]);
    if(Max[u] < Max[rt]) rt = u;
}
void findrt(int u, int fa) {
    tsz = (sz[u] == 0 ? n : sz[u]);
    Max[rt = 0] = INF;
    getrt(u, fa);
}
void dfs(int u, int fa) {
    vis[u] = true;
    for(auto it : G[u]) {
        int v = it.fi;
        if(v != fa && !vis[v]) {
            findrt(v, u);
            father[rt] = u;
            dfs(rt, 0);
        }
    }   
}

int f[N][20], deep[N], d[N];
void dfs2(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 it : G[u]) {
        int v = it.fi, w = it.se;
        if(v != fa) {
            d[v] = d[u] + w;
            dfs2(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 dis(int x, int y) {
    int z = LCA(x, y);
    return d[x] + d[y] - 2 * d[z];   
} 

int minv[MAX], ls[MAX], rs[MAX];
int root[N], tot;

void update(int &o, int l, int r, int p, int v) {
    if(!o) o = ++tot;   
    if(l == r) {
        minv[o] = min(minv[o], v);
        return;
    }
    int mid = (l + r) >> 1;
    if(p <= mid) update(ls[o], l, mid, p, v);
    else update(rs[o], mid + 1, r, p, v);
    minv[o] = min(minv[ls[o]], minv[rs[o]]);
}
void update(int x, int p) {
    for(int o = x; o; o = father[o]) {
        update(root[o], 1, 10000, p, dis(x, o));
    }
}

int query(int o, int l, int r, int L, int R) {
    if(!o) return INF;
    if(L <= l && r <= R) {
        return minv[o];   
    }
    int mid = (l + r) >> 1, ans = INF;
    if(L <= mid) ans = query(ls[o], l, mid, L, R);
    if(R > mid) ans = min(ans, query(rs[o], mid + 1, r, L, R));
    return ans;
}
int query(int x, int L, int R) {
    int ans = INF;
    for(int o = x; o; o = father[o]) {
        ans = min(ans, dis(x, o) + query(root[o], 1, 10000, L, R));
    }
    return ans;
}

int a[N];
void run() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i < n; i++) {
        int u, v, w; cin >> u >> v >> w;
        G[u].push_back(MP(v, w));
        G[v].push_back(MP(u, w));
    }
    dfs2(1, 0);
    findrt(1, 0); 
    dfs(rt, 0); 
    memset(minv, INF, sizeof(minv));
    for(int i = 1; i <= n; i++) {
        update(i, a[i]);
    }
    while(m--) {
        int op, u, x; cin >> op >> u >> x;
        if(op == 1) {
            update(u, x);
        } else {
            int y; cin >> y;
            int ans = query(u, x, y);
            if(ans == INF) cout << -1 << '\n';
            else cout << 2 * ans << '\n';
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
posted @ 2020-04-16 21:00  heyuhhh  阅读(172)  评论(0编辑  收藏  举报