20200515 河北工业大学ACM校队五月选拔赛 题解

Posted hebut-amadeus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20200515 河北工业大学ACM校队五月选拔赛 题解相关的知识,希望对你有一定的参考价值。

作为本场被请来(抓来)验题的人,我也是见证了这次比赛赛前裁判组的准备,尤其是icpc的系统,真的很不容易,在此为大家点个赞

验题的代码我就放到我的博客上了

 

A

输出Hl即可

#include<bits/stdc++.h>
#define LL long long
#define maxn 100010
using namespace std;

int main(){
    printf("Hl
");
    return 0;
} 

 

B

推公式题

对于连续抽i张卡,对答案的贡献为n^i

易推出

当m=1时答案是n

否则答案是m的n次方-1再除以m-1

#include<bits/stdc++.h>
#define LL long long
#define maxn 100010
using namespace std; 

LL qpow(LL n, LL c){
    LL base = n, ret = 1;
    while(c){
        if(c & 1){
            ret = ret * base;
        }
        base = base * base;
        c = c >> 1;
    }
    return ret;
}

int main(){
    LL m, n;
    while(~scanf("%lld %lld", &n, &m)){
        if(m == 1){
            printf("%lld
", n);
        }
        else{
            printf("%lld
", (qpow(m, n) - 1) / (m - 1));
        }
    }
}

 

C

用dfs搜索即可,注意边界条件

#include<bits/stdc++.h>
#define LL long long
#define maxn 110
using namespace std;

char a[maxn][maxn];

bool vis[maxn][maxn];

int n, m;

void _dfs(int x, int y){
    if(x < 0 || x >= n || y < 0 || y >= m){
        return;
    }
    if(a[x][y] != @){
        return;
    }
    if(vis[x][y]){
        return;
    }
    vis[x][y] = 1;
    _dfs(x + 1, y);
    _dfs(x - 1, y);
    _dfs(x, y + 1);
    _dfs(x, y - 1);
    _dfs(x + 1, y - 1);
    _dfs(x + 1, y + 1);
    _dfs(x - 1, y - 1);
    _dfs(x - 1, y + 1);
    return;
}

int main(){
    cin >> n >> m;
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            cin >> a[i][j];
        }
    }
    int ans = 0;
    memset(vis, 0, sizeof vis);
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            if(a[i][j] == @ && !vis[i][j]){
                ans = ans + 1;
                _dfs(i, j);
            }
        }
    }
    cout << ans << endl;
    return 0;
}

 

D

最短路问题,数据很水,有n^3的算法卡过去了

#include<bits/stdc++.h>
#define LL long long
#define maxn 1000010
using namespace std;

struct Edge{
    int t;
    LL c;
    Edge(){}
    Edge(int _t, LL _c){
        t = _t;
        c = _c;
    }
    bool operator < (Edge e) const{
        return c > e.c;
    }
};

LL dis[maxn];

priority_queue<Edge> p;

vector<Edge> ed[maxn];

bool vis[maxn];

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    int f, t, c;
    for(int i = 0; i < m; ++i){
        scanf("%d %d %d", &f, &t, &c);
        ed[f].push_back(Edge(t, c));
    }
    int S, T;
    scanf("%d %d", &S, &T);
    p.push(Edge(S, 0));
    for(int i = 1; i <= n; ++i){
        dis[i] = -1;
        vis[i] = 0;
    }
    while(!p.empty()){
        Edge now = p.top();
        p.pop();
        if(vis[now.t]){
            continue;
        }
        vis[now.t] = 1;
        dis[now.t] = now.c;
        for(int i = 0; i < ed[now.t].size(); ++i){
            p.push(Edge(ed[now.t][i].t, ed[now.t][i].c + now.c));
        }
    }
    printf("%lld
", dis[T]);
    return 0;
}

 

E

本场hard难度的题之一,其实也就是俩板子拼一下

先用tarjan缩点,然后在新图里跑dijkstra(其实spfa也没卡)

#include<bits/stdc++.h>
#define LL long long
#define maxn 1000010
using namespace std;

struct Node{
    int t, c;
    Node(int _t = 0, int _c = 0){
        t = _t;
        c = _c;
    }
    bool operator < (Node n) const{
        return c > n.c;    
    }
};

vector<Node> ed1[maxn], ed2[maxn];

int sd[maxn], dis[maxn], dfn[maxn], low[maxn], cnt, th;

bool vis[maxn];

stack<int> s;

void tarjan(int u) 
{
    dfn[u] = low[u] = ++cnt;
    vis[u] = 1; 
    s.push(u);
    for(int i = 0; i < ed1[u].size(); ++i){
        int v = ed1[u][i].t;
        if(!dfn[v]){  
            tarjan(v);
            low[u] = min(low[u],low[v]); 
        }
        else if(vis[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(dfn[u] == low[u]){
        th++;
        while(1){
            int v = s.top();
            s.pop();
            vis[v] = 0;
            sd[v] = th;
            if(u == v) break;
        }
    }
}

priority_queue<Node> p;

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    int f, t, c;
    for(int i = 0; i < m; ++i){
        scanf("%d %d %d", &f, &t, &c);
        if(f == t){
            continue;
        }
        ed1[f].push_back(Node(t, c));
    }
    memset(vis, 0, sizeof vis);
    cnt = 0, th = 0;
    for(int i = 1; i <= n; i++){
         if(!dfn[i]){
            tarjan(i);
         }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 0; j < ed1[i].size(); ++j){
            ed2[sd[i]].push_back(Node(sd[ed1[i][j].t], ed1[i][j].c));
        }
    }
    int S, T;
    scanf("%d %d", &S, &T);
    p.push(Node(sd[S], 0));
    for(int i = 1; i <= n; ++i){
        dis[i] = -1;
        vis[i] = 0;
    }
    while(!p.empty()){
        Node now = p.top();
        p.pop();
        if(vis[now.t]){
            continue;
        }
        vis[now.t] = 1;
        dis[now.t] = now.c;
        for(int i = 0; i < ed2[now.t].size(); ++i){
            p.push(Node(ed2[now.t][i].t, ed2[now.t][i].c + now.c));
        }
    }
    printf("%d
", dis[sd[T]]);
    return 0;
} 

 

F

我们遍历2到n-1,对每个n能取模为0的数,判断是否为质数,然后按格式输出就行了

#include<bits/stdc++.h>
#define LL long long
#define maxn 100010
using namespace std;

bool isPrime(int n){
    for(int i = 2; i <= sqrt(n); ++i){
        if(n % i == 0){
            return false;
        }
    }
    return true;
}

string IntToString(int n){
    string ret = "";
    while(n){
        ret.push_back(char((n % 10) + 0));
        n = n / 10;
    }
    reverse(ret.begin(), ret.end());
    return ret;
}

vector<int> v;

int main(){
    int n;
    scanf("%d", &n);
    for(int i = 2; i <= n; ++i){
        if(n % i == 0 && isPrime(i)){
            v.push_back(i);
        }
    }
    string ans = "";
    ans = ans + IntToString(n);
    for(int i = 0; i < v.size(); ++i){
        ans = ans + "*(1-1/" + IntToString(v[i]) + ")";
    }
    cout << ans << endl;
    return 0;
} 

 

G

就找个最大值就行了

#include<bits/stdc++.h>
#define LL long long
#define maxn 100010
using namespace std; 

int main(){
    int n;
    cin >> n;
    int t, ma = -100;
    for(int i = 0; i < n * n; ++i){
        cin >> t;
        ma = max(t, ma);
    }
    cout << ma << endl;
    return 0;
}

 

H

我们先打质数表,然后算前缀和

对i,如果i是质数,那么pre[i] = pre[i - 1] + 1;

否则pre[i] = pre[i - 1];

然后对每组l跟r,前缀和做差就行了

#include<bits/stdc++.h>
#define LL long long
#define maxn 1000010
using namespace std; 

bool isp[maxn];

int prep[maxn];

int main(){
    for(int i = 2; i < maxn; ++i){
        if(!isp[i]){
            for(int j = i + i; j < maxn; j = j + i){
                isp[j] = 1;
            }
        }
        prep[i] = prep[i - 1] + (isp[i] ^ 1);
    }
    int q;
    scanf("%d", &q);
    int l, r;
    while(q--){
        scanf("%d %d", &l, &r);
        if(l == 0){
            printf("%d
", prep[r]);
        }
        else{
            printf("%d
", prep[r] - prep[l - 1]);
        }
    }
    return 0;
}

 

I

拿来防AK的,我们先推dp式子,容斥算出六种置换中不动点分别有多少

然后使用牛顿迭代推F(x^2),F(x^3),套FFT即可

具体可以看这一篇:https://baka.online/%E7%83%B7%E5%9F%BA%E8%AE%A1%E6%95%B0-burnside%E5%BC%95%E7%90%86%E7%94%9F%E6%88%90%E5%87%BD%E6%95%B0fft/

#include <bits/stdc++.h>

typedef long long ll;

const int N = 8e5 + 233, P = 998244353;

inline ll fpow(ll x, int y) {
  ll ret = 1;
  for ( ; y; y >>= 1, x = x * x % P)
    if (y & 1) ret = ret * x % P;
  return ret;
}

const int Gp = 3, Gpi = fpow(Gp, P - 2);

int rev[N], L, lim;

inline void init(int n) {
  L = 0, lim = 1;
  while (lim < n) lim <<= 1, ++L;
  for (int i = 1; i < lim; ++i)
    rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1));
}

inline void NTT(ll f[], int o) {
  for (int i = 1; i < lim; ++i)
    if (i < rev[i])
      std::swap(f[i], f[rev[i]]);
  for (int i = 1; i < lim; i <<= 1) {
    ll T = fpow(o == 1 ? Gp : Gpi, (P - 1) / (i << 1));
    for (int j = 0; j < lim; j += i << 1) {
      ll w = 1;
      for (int k = 0; k < i; ++k, w = w * T % P) {
        ll nx = f[j + k], ny = f[i + j + k] * w % P;
        f[j + k] = nx + ny >= P ? nx + ny - P : nx + ny;
        f[i + j + k] = nx - ny >= 0 ? nx - ny : nx - ny + P;
      }
    }
  }
  if (o == -1) {
    ll inv = fpow(lim, P - 2);
    for (int i = 0; i < lim; ++i)
      f[i] = f[i] * inv % P;
  }
}

void get_inv(ll F[], ll G[], int n) {
  if (n == 1) {
    G[0] = fpow(F[0], P - 2);
    return;
  }
  get_inv(F, G, (n + 1) >> 1);
  static ll T[N];
  init(n << 1);
  for (int i = 0; i < lim; ++i)
    T[i] = i < n ? F[i] : 0;
  NTT(G, 1), NTT(T, 1);
  for (int i = 0; i < lim; ++i)
    G[i] = ((2 - G[i] * T[i]) % P + P) * G[i] % P;
  NTT(G, -1);
  for (int i = n; i < lim; ++i)
    G[i] = 0;
}

int n; ll A[N], B[N], C[N], D[N], E[N], F[N];

void solve(int n) {
  if (n == 1) return A[0] = 1, void();
  solve((n + 1) >> 1); init(n << 1);
  for (int i = 0; i < lim; ++i)
    B[i] = C[i] = D[i] = E[i] = F[i] = 0;
  for (int i = 0; i < n; ++i) {
    if (i * 2 < n) B[i * 2] = A[i];
    if (i * 3 < n) C[i * 3] = A[i];
  }
  NTT(A, 1), NTT(B, 1), NTT(C, 1);
  for (int i = 0; i < lim; ++i) {
    D[i] = (A[i] * A[i] % P * A[i] % P + 3 * B[i] * A[i] % P + 2 * C[i]) % P;
    E[i] = (3 * A[i] * A[i] % P + 3 * B[i]) % P;
  }
  NTT(A, -1), NTT(D, -1), NTT(E, -1);
  for (int i = lim - 1; i; --i)
    D[i] = D[i - 1], E[i] = E[i - 1];
  D[0] = 6; E[0] = P - 6;
  for (int i = 0; i < lim; ++i)
    D[i] = (D[i] - 6 * A[i] % P + P) % P;
  get_inv(E, F, lim);
  NTT(D, 1), NTT(F, 1);
  for (int i = 0; i < lim; ++i)
    F[i] = (P - D[i] * F[i] % P) % P;
  NTT(F, -1);
  for (int i = 0; i < lim; ++i)
    A[i] = (A[i] + F[i]) % P;
  for (int i = n; i < lim; ++i)
    A[i] = 0;
}

int main() {
  std::cin >> n;
  init(n);
  solve(n + 1);
  std::cout << A[n] << std::endl;
  return 0;
}

 

希望大家玩的开心!

以上是关于20200515 河北工业大学ACM校队五月选拔赛 题解的主要内容,如果未能解决你的问题,请参考以下文章

一些杂七杂八的东西。。。

zju 校队选拔 被虐记

A777 吉林大学ACM集训队选拔赛(重现赛)

武汉工程大学第三届ACM程序设计选拔赛(正式赛)题解

2019年华南理工大学软件学院ACM集训队选拔赛 Round1

2018年北京信息科技大学第十届程序设计竞赛暨ACM选拔赛-B-precise math function