把会的题放一起吧,不然字数太少了。

A. 论如何出一道水题

Description

给定 n,求一对整数 (i,j),在满足 1 ≤ i ≤ j ≤ n 且 \gcd(i,j)=1gcd(i,j)=1 的前提下,要求最大化 i+j 的值

Solution

打表,然后找到规律

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>

using namespace std;

const int mod = 100000000;
const int N = 5e5 + 5;
typedef long long ll;
const double pi = acos(-1.0);
const double EPS=1e-8;


int main() {
  ll n;
  while(cin >> n) {
    if(n == 1) {
      cout << 2 << "\n";
    } else {
      cout << 2 * n - 1 << "\n";
    }
    // ll ans = -1;
    // for(int i = 1; i <= n; i++) {
    //   for(int j = 1; j <= n; j++) {
    //     if(__gcd(i, j) == 1) {
    //       ans = max(ans, 1LL * (i + j));
    //     }
    //   }
    // }
    // cout << ans << "\n";
  }
  return 0;
}

B. 暴力出奇迹

Description

给定一个序列,寻找一对l,r,满足1 ≤ l ≤ r ≤ n

Solution

一开始看错题,以为是要最大化区间与的值, 不过照着这个思路,对于每一个二进制位,我们以当前点为右端点,找连续的该位为1的段的最左端点,按左端点排序,从左到右取贡献。

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>

using namespace std;

const int mod = 100000000;
const int N = 1e5 + 5;
typedef long long ll;
const double pi = acos(-1.0);
const double EPS=1e-8;

ll dp[N][32], sum[N], a[N];
struct node {
  int l, val;
  node(int _l = 0, int _val = 0): l(_l), val(_val){}
  bool operator < (const node &s) const {
    return l < s.l;
  }
};
int main() {
  int n;
  cin >> n;
  for(int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1] + a[i];
  for(int i = 1; i <= n; i++) {
    for(int j = 0; j < 32; j++) {
      dp[i][j] = 0;
      if(a[i] & (1LL << j)) {
        dp[i][j] = dp[i - 1][j] + 1;
      }
    }
  }
  ll ans = 0;
  for(int i = 1; i <= n; i++) {
    vector<node> v(50);
    for(int j = 0; j < 32; j++) {
      ll p = (1LL << j);
      v[j] = node(i - dp[i][j], p);
    }
    sort(v.begin(), v.end());
    ll now = 0;
    for(int j = 0; j < 32; j++) {
      now ^= v[j].val;
      ans = max(ans, 1LL * now * (sum[i] - sum[v[j].l]));
    }
  }
  cout << ans << "\n";
  return 0;
}

E. 跳石头

Description

一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

Description

看了一堆人过,就知道是水题,求最大最小值,简单思路就是二分,二分答案,发现相邻距离比答案小,就要去掉石头。

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>

using namespace std;

const int mod = 100000000;
const int N = 1e5 + 5;
typedef long long ll;
const double pi = acos(-1.0);
const double EPS=1e-8;

int a[N];
int l, n, m;
bool check(int x) {
  int res = 0;
  int now = 0;
  for(int i = 1; i <= n; i++) {
    if(a[i] - now < x) {
      res++;
    } else {
      now = a[i];
    }
  }
  return res <= m;
}
int main() {
  cin >> l >> n >> m;
  for(int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  int left = 1, right = l;
  a[n + 1] = l;
  n++;
  int ans = -1;
  while(left <= right) {
    int mid = left + right >> 1;
    if(check(mid)) {
      left = mid + 1;
      ans = mid;
    } else {
      right = mid - 1;
    }
  }
  cout << ans << "\n";
  return 0;
}

F. 树上求和

Description

有一棵包含n个节点和n-1条边的树,规定树链(u,v)为树上从u到v的简单路径。
树的每条边上都有一个正整数,这个正整数被称作这条边的颜色,规定一条树链的权值w(u,v)为这条树链上所有边的颜色的代数和。
而整棵树的权值为所有不同的树链的权值的代数和。
已知所有边的颜色集合恰好为1到n-1这n-1个不同的正整数,请你为每条边安排一种颜色,使得这棵树的权值尽量小,你不需要给出具体方案,只需要求出这个最小的权值即可。

Solution

一开始想的是先从叶子染色(类似拓扑排序的思想) 然后贴个树上任意两点距离和的板子(只能过60%样例
正确做法:树形dp处理出每条路径要走的次数, 排序后赋值即可
那么如何处理出要走的次数呢?
考虑sum[i] 是以i为根的子树拥有的节点数
那么假设把这个点作为树上的划分点,左边有sum[i]个点,右边有n - sum[i]个点
显然经过这个点的次数是左边的点去往右边的点的路径数量,即sum[i] * (n - sum[i])

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e5 + 5;
const ll mod = 998244353;
const int DEG = 30;
const double PI = acos(-1.0);
const double eps = 1e-12;
const ll inf = 1e18;
static auto fast_iostream = [](){
  ios::sync_with_stdio(false);
  cout.tie(nullptr);
  return 0;
}();
struct Edge{
    int v, nextz;
    ll w;
}E[N << 1];
int head[N], tot;
void init(){
    memset(head, -1, sizeof(head));
    tot = 0;
}
int n;
ll sum[N];
void add(int u, int v, ll w){
    E[tot].v = v;
    E[tot].w = w;
    E[tot].nextz = head[u];
    head[u] = tot++;
}
ll dp[N], sz[N], val[N];
ll ans = 0;
void dfs(int u, int fa) {
    sum[u] = 1;
    for (int i = head[u]; ~i; i = E[i].nextz) {
        int v = E[i].v;
        if (v == fa)
            continue;
        dfs(v, u);
        sum[u] += sum[v];
    }
}
ll cal[N];
void solve(int u, int fa) {
    for (int i = head[u]; ~i; i = E[i].nextz) {
        int v = E[i].v;
        ll w = E[i].w;
        if (v == fa)
            continue;
        cal[w] += sum[v] * (n - sum[v]);
        solve(v, u);
    }
}
bool vis[N];
int se[N];
int main(){
  init();
  cin >> n;
  for(int i = 1; i <= n - 1; i++){
    int u, v;
    cin >> u >> v;
    add(u, v, i);
    add(v, u, i);
  }
  dfs(1, -1);
  solve(1, -1);
  ll ans = 0;
  for(int i = 1; i <= n - 1; i++){
    se[i] = i;
  }
  sort(se + 1, se + n, [](int x, int y){return cal[x] < cal[y];});
  ll color = n - 1;
  for(int i = 1; i <= n - 1; i++){
    ans += color * cal[se[i]];
    color--;
  }
  cout << ans << "\n";
  return 0;
}