A. Maximize The Beautiful Value

Solution

看了半天才看懂,forward向前是从右往左挪动,我们知道一个数字往前挪动k个位后,位置为i - k。
注意到i - k 到 i - 1 这个范围的数字他们都会相应往后挪,即我们的答案会加上sum[i - 1] - sum[i - k - 1]
因此,只需要从k + 1位开始枚举每一位,求最大值就行了

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e5 + 5;
const ll mod = 7;
const int DEG = 20;
const double PI = acos(-1.0);
const double eps = 1e-10;
const ll inf = 1e18;
static auto faster_iostream = []() {
    std::ios::sync_with_stdio(false);  // turn off sync
    std::cin.tie(nullptr);             // untie in/out streams
    return 0;
}();
ll a[N];
ll sum[N];
int main(){
  int t;
  cin >> t;
  while(t--){
    int n, k;
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> a[i];
    ll ans = 0;
    for(int i = 1; i <= n; i++) ans += i * a[i], sum[i] = sum[i - 1] + a[i];
    ll res = 0;
    for(int i = k + 1; i <= n; i++){
      int now = i - k; //往前k位
      res = max(res, ans - i * a[i] + now * a[i] + sum[i - 1] - sum[i - 1 - k]); //减去原来的贡献 加上现在的贡献和中间数字往前挪得到的贡献
    }
    cout << res << "\n";
  }
  return 0;
} 

B. 身体训练

Solution

对于一个配送员,它在每个位置的概率是, 位置只会改变配送员的速度,要走的距离永远是u*n。
我们计算每个配送员在每个位置所需的时间并累加,最后除以n就是答案
得到

Code

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e5 + 5;
const ll mod = 7;
const int DEG = 20;
const double PI = acos(-1.0);
const double eps = 1e-10;
const ll inf = 1e18;
static auto faster_iostream = []() {
    std::ios::sync_with_stdio(false);  // turn off sync
    std::cin.tie(nullptr);             // untie in/out streams
    return 0;
}();
double c[N], d[N];
int main(){
  int n;
  double v, u;
  cin >> n >> v >> u;
  for(int i = 1; i <= n; i++){
    cin >> c[i];
  }
  for(int i = 1; i <= n; i++){
    cin >> d[i];
  }
  double ans = 0;
  for(int i = 1; i <= n; i++){
    for(int j = 1; j <= n; j++){
      ans += u / (c[i] - (j - 1) * d[i] - v);
    }
  }
  cout << fixed << setprecision(3) << ans << "\n";
  return 0;
}

C. Borrow Classroom

Solution

树上距离的问题首先想到LCA,我们先求出B -> C -> 1的距离 dist1 和 A -> 1的距离 dist2
如果 dist1 < dist2 显然抓不到
如果 dist1 > dist2 显然老师直接去1堵他了hhh
如果 dist1 == dist2 就要讨论一下了
1.若 lca(a,c) == 1 则他们俩最终会在1相遇,由题意知小Q同学手速很快,老师gg
2.否则老师可以到lca(a, c)点堵他

Code

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e5 + 5;
const ll mod = 7;
const int DEG = 20;
const double PI = acos(-1.0);
const double eps = 1e-10;
const ll inf = 1e18;
static auto faster_iostream = []() {
    std::ios::sync_with_stdio(false);  // turn off sync
    std::cin.tie(nullptr);             // untie in/out streams
    return 0;
}();
struct Edge{
    int to, nextz;
}edge[N << 1];
int tot, head[N];
void addedge(int u, int v){
    edge[tot].to = v;
    edge[tot].nextz = head[u];
    head[u] = tot++;
}
void init(){
    memset(head, -1, sizeof(head));
    tot = 0;
}
int fa[N][DEG];
int dist[N];
int deg[N];
void bfs(int root){
    fa[root][0] = root;
    deg[root] = 0;
    dist[root] = 0;
    queue<int> que;
    que.push(root);
    while(!que.empty()){
        int tmp = que.front();
        que.pop();
        for(int i = 1; i < DEG; i++){
            fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];
        }
        for(int i = head[tmp]; i != -1; i = edge[i].nextz){
            int v = edge[i].to;
            if(v == fa[tmp][0]) continue;
            que.push(v);
            deg[v] = deg[tmp] + 1;
            dist[v] = dist[tmp] + 1;
            fa[v][0] = tmp;
        }
    }
}
int LCA(int u, int v){
    if(deg[u] > deg[v]) swap(u, v);
    int hu = deg[u], hv = deg[v];
    int tu = u, tv = v;
    for(int det = hv - hu, i = 0; det; det >>= 1, i++){
        if(det & 1){
            tv = fa[tv][i];
        }
    }
    if(tu == tv){
        return tu;
    }
    for(int i = DEG - 1; i >= 0; i--){
        if(fa[tu][i] == fa[tv][i]) continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}
int main(){
  int t;
  cin >> t;
  while(t--){
    init();
    int n, q;
    cin >> n >> q;
    for(int i = 1; i <= n - 1; i++){
      int u, v;
      cin >> u >> v;
      addedge(u, v);
      addedge(v, u);
    }
    bfs(1);
    while(q--){
      int a, b, c;
      cin >> a >> b >> c;
      if(b == c && b == 1){
        cout << "NO\n";
        continue;
      }
      int dist1 = dist[b] + dist[c] - 2 * dist[LCA(b, c)] + dist[c]; // b -> c -> 1
      int dist2 = dist[a]; 
      if(dist1 < dist2){
        cout << "NO\n";
      }
      else if(dist1 > dist2){
        cout << "YES\n";
      }
      else {
        if(LCA(c, a) == 1) cout << "NO\n";
        else cout << "YES\n";
      }
    }
  }
  return 0;
}

D. 景区路线规划

Solution

这题比赛的时候做的人挺少的,赛后补题的人也挺少,比赛的时候没看题,以为是个图论+期望挺难的
其实不是很复杂,我们考虑对于每个点,统计当前能到达的其他点的可行方案并计算期望
因为到达其他的概率是相同的,所以只需要一边统计方案数,一边统计ans,最后令ans / cnt 即可
注意到n, k的数据范围,可以记忆化搜索,至于一开始怎么取点,我们可以考虑建立一个超级源点0
令0点到任意一个点的时间都是0即可

Code

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e5 + 5;
const ll mod = 1e9 + 7;
const int DEG = 20;
const double PI = acos(-1.0);
const double eps = 1e-10;
const ll inf = 1e18;
static auto faster_iostream = []() {
    std::ios::sync_with_stdio(false);  // turn off sync
    std::cin.tie(nullptr);             // untie in/out streams
    return 0;
}();
struct node{
  int c, h1, h2;
}a[N];
struct Edge{
  int v, nextz;
  int w;
}edge[N << 1];
int head[N], tot; 
void addedge(int u, int v, int w){
  edge[tot].v = v;
  edge[tot].w = w;
  edge[tot].nextz = head[u];
  head[u] = tot++;
}
int n, m, k;
double dp[1005][1005];
bool vis[1005][1005];
double dfs(int u, int t, int x){
  if(vis[u][t]) return dp[u][t]; // 记忆化
  vis[u][t] = true;
  int p = (x == 0) ? a[u].h1 : a[u].h2; // 该点对男/女的满意度
  double ans = 0;
  double cnt = 0;
  for(int i = head[u]; i != -1; i = edge[i].nextz){
    int cost = edge[i].w;
    int v = edge[i].v;
    if(cost + a[v].c <= t){
      cnt++;
      ans += dfs(edge[i].v, t - (cost + a[v].c), x); // 统计答案
    }
  }
  if(cnt){
    ans /= cnt; // 如果存在方案,除以方案数
  }
  ans += p; // 不要忘记加上这个点本身的贡献
  dp[u][t] = ans; // 记忆化
  return ans;
}
double solve(int x){ // x = 0 男  x = 1 女
  memset(dp, 0, sizeof(dp));
  memset(vis, 0, sizeof(vis));
  return dfs(0, k, x); 
}
int main(){
  tot = 0;
  memset(head, -1, sizeof(head));
  cin >> n >> m >> k;
  for(int i = 1; i <= n; i++){
    cin >> a[i].c >> a[i].h1 >> a[i].h2;
  }
  while(m--){
    int u, v, w;
    cin >> u >> v >> w;
    addedge(u, v, w);
    addedge(v, u, w);
  }
  for(int i = 1; i <= n; i++){
    addedge(0, i, 0); // 0作为超级源点
  }
  cout << fixed << setprecision(5) << solve(0) << ' ' << solve(1) << "\n";
  return 0;
}

E. 幸运数字Ⅱ

Solution

日常梦游,先打个表,把所有幸运数字列出来,也就几千个数字。
然后我们可以分成一个个块。
这些块的贡献为块的大小 * 大于等于这个块的幸运数字
注意打表最好多打几个,不然可能会跟我一样RE,还以为是dfs爆栈
模拟即可,具体看代码

Code

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e5 + 5;
const ll mod = 7;
const int DEG = 20;
const double PI = acos(-1.0);
const double eps = 1e-10;
const ll inf = 1e18;
static auto faster_iostream = []() {
    std::ios::sync_with_stdio(false);  // turn off sync
    std::cin.tie(nullptr);             // untie in/out streams
    return 0;
}();
ll dp[N];
int cnt = 0;
void bfs(){
  queue<ll> q;
  q.push(4);
  q.push(7);
  while(!q.empty()){
    ll now = q.front();
    q.pop();
    if(now > 1e12) continue; // 这里要多打几个 不然会RE 我还以为dfs爆栈了
    dp[++cnt] = now;
    q.push(now * 10 + 4);
    q.push(now * 10 + 7);
  }
}
ll solve(ll x){
  int now = 1;
  ll p = min(dp[now], x); // 块的终点
  ll res = 0;
  ll last = 1;
  while(p < x){
    res += (p - last + 1) * dp[now]; // 完整的块贡献 比如 5 - 7  8 - 44
    last = dp[now] + 1;
    if(dp[now + 1] < x){
      now++;
      p = dp[now];
    }
    else p = x; 
  }
  if(!res) now--; //如果res == 0, 说明循环都没进去 则这个 x 小于等于4, 要特判下
  res += (p - last + 1) * dp[now + 1]; // x作为块的终点
  return res;
}
int main(){
  bfs(); // 打表
  sort(dp + 1, dp + 1 + cnt);
  ll l, r;
  cin >> l >> r;
  cout << solve(r) - solve(l - 1) << "\n";
}