2024年1月11日总结

发布时间 2024-01-11 19:48:06作者: liuyichen

1

题目

思路

\(i * j\) 是完全平方数。

那么 \(\sqrt{i * j}\) 是一个整数。令 \(p ( p\)\(i\) 中最大平方因子, \(x = i / (p * p)\), \(q * q\)\(j\) 中的最大平方因子, \(x = j / (q * q)\)

那么 \(\sqrt{i * j}\) 可以写成 \(pq\sqrt{xy}\), 如果 \(\sqrt{i * j}\) 是一个整数, 那么 \(\sqrt{xy}\) 也是一个整数, \(x, y\) 不包含其他完全平方因子, 相等条件只有 \(x = y\), 前缀统计当前数除以最大平方因子的数, 维护答案即可

时间复杂度 \(O(N \log N)\)

空间复杂度 \(O(N)\)

#include<bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;

long long ans, op, id, dp[N];
int n;

int C(int x){
  op = 1;
  for(int i = 1; i * i <= x; ++i){
    if(x % (i * i) == 0){
      op = i * i;
    }
  }
  return op;
}

int main(){
  cin >> n;
  for(int i = 1; i <= n; ++i){
    ans++;
    id = C(i);
    ans += dp[i / id] * 2;
    dp[i / id]++;
  }
  cout << ans;
  return 0;
}

2

题目

思路

可以发现, 可以枚举两次充能的节点。分段DP

状态 \((i, sum)\) 表示解决完前 \(i\) 个打开点并返回 \(sx, sy\) 处是的能量为 \(sum\)

拓扑序 \(i\) 从小到大

最优话属性 \(sum\), \(i\) 一定时, \(sum\) 越小越好

\(dis(a, b)\) 表示 \(a \to b\) 打开点的欧几里得距离, \(sx, sy\) 为编号为 \(0\) 的打开点

转移 \(dp_{i} = \min \limits_{j = i - k}^{i - 1} \{dp_{j} + \sum \limits_{k = j + 1}^{i - 2} dis(j, j + 1) + dis(j + 1, 0) + dis(i, 0)\}\)

显然 \(\sum \limits_{k = j + 1}^{i - 2} dis(j, j + 1)\) 可以前缀和优化

时间复杂度 \(O(N^2)\)

空间复杂度 \(O(N)\)

\(S_i\) 表示按顺序从 \(1\) 号打卡点到第 \(i\) 号打卡点的距离

优化转移可得 \(dp_{i} = dis(0, i) + S_i + \min \limits_{j = i - k}^{i - 1} \{dp_{j} - S_{j + 1} + dis(0, j + 1)\}\)

单调队列优化。

时间复杂度 \(O(N)\)

空间复杂度 \(O(N)\)

#include<bits/stdc++.h>

using namespace std;

const long long N = 2e5 + 5;

long long n, k, h, t, q[N];

long long x[N], y[N];

long double S(long long i, long long j){
  return sqrt(1ll * abs(x[i] - x[j]) * abs(x[i] - x[j]) + 1ll * abs(y[i] - y[j]) * abs(y[i] - y[j]));
}

long double sum[N], dp[N];

int main(){
  cin >> n >> k;
  for(long long i = 0; i <= n; ++i){
    cin >> x[i] >> y[i];
    if(i){
      sum[i] = sum[i - 1] + S(i, i - 1);
    }
  }
  h = 1, t = 1;
  for(long long i = 1; i <= n; ++i){
    for(; h <= t && q[h] < i - k; h++){
    }
    dp[i] = dp[q[h]] + sum[i] - sum[q[h] + 1] + S(q[h] + 1, 0) + S(i, 0);
    for(; h <= t && dp[q[t]] - sum[q[t] + 1] + S(q[t] + 1, 0) >= dp[i] - sum[i + 1] + S(i + 1, 0); t--){
    }
    q[++t] = i;
  }
  cout << fixed << setprecision(6) << dp[n];
  return 0;
}

3

题目

思路

可以发现, 只有\(4 \cdot N+1\)个点可以改变反向, 所以只需要对于每个有效点, 做bfs即可

怎么转移 ?

当前位置可以向四个反向滑行, 只有滑到障碍物才停, 可以set记录每个障碍物的位置, 转移二分即可。

时间复杂度 \(O(4 \cdot N)\)

空间复杂度 \(O(4 \cdot N)\)

#include<bits/stdc++.h>

using namespace std;

const int N = 5e6;

map<int, set<int>>hx, hy;

map<int, map<int, int>>dp, vis;

int x, y, h, w, n, sx, sy, gx, gy, t;

struct O{
  int x, y;
}q[N];

void C(int x, int y, int md){
  if(vis[x][y]){
    return;
  }
  //cout << x << ' ' << y << '\n';
  vis[x][y] = 1;
  dp[x][y] = md;
  q[++t] = {x, y};
}

int main(){
  cin >> h >> w >> n >> sx >> sy >> gx >> gy;
  for(int i = 1; i <= n; ++i){
    cin >> x >> y;
    vis[x][y] = 1;
    hx[x].insert(y), hy[y].insert(x);
  }
  C(sx, sy, 0);
  h = 1;
  for(; h <= t;){
    x = q[h].x, y = q[h++].y;
    auto it1 = hx[x].lower_bound(y);
    auto it2 = hy[y].lower_bound(x);
    if(it1 != hx[x].begin()){
      C(x, *prev(it1) + 1, dp[x][y] + 1);
    }
    if(it1 != hx[x].end()){
      C(x, *it1 - 1, dp[x][y] + 1);
    }
    if(it2 != hy[y].begin()){
      C(*prev(it2) + 1, y, dp[x][y] + 1);
    }
    if(it2 != hy[y].end()){
      C(*it2 - 1, y, dp[x][y] + 1);
    }
  }
  cout << (!vis[gx][gy] ? -1 : dp[gx][gy]);
  return 0;
}