ABC384题解

[ABC384C] Perfect Standings

题面翻译

Takahashi 决定举办一次编程竞赛。

竞赛包含五道题目:A、B、C、D、E,得分分别为 \(a\)\(b\)\(c\)\(d\)\(e\)

共有 \(31\) 名参赛者,每人都至少解答了一道题目。

更具体地说,对于字符串 ABCDE 的每个非空子序列(不一定连续),都有一个以该子序列命名的参赛者,他解答了与其名字中字母对应的题目,而没有解答其他题目。

例如,参赛者 A 只解答了题目 A,参赛者 BCE 解答了题目 B、C 和 E。

输入格式

$ a $ $ b $ $ c $ $ d $ $ e $

输出格式

按得分从大到小的顺序打印参赛者的姓名。参与者获得的分数是他们所解决问题的分数的总和。

如果两个参与者获得的分数相同,则首先打印名字在字典中较小的参与者。

制約

  • $ 100\leq\ a\leq\ b\leq\ c\leq\ d\leq\ e\leq\ 2718 $
  • 入力はすべて整数

解题思路

//abc384c
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int grade[10], idx = 0;
bool flag[10];
string s1[5] = {"A", "B", "C", "D", "E"};
struct st{
    string a;
    int val;
}c[50];

bool cmp(st x, st y){
    if(x.val == y.val)      return x.a < y.a;
    else    return x.val > y.val;
}

//递归函数包括,当前枚举到第几个字母,当前枚举构成的字符串,当前构成字符串的值
void f(int alpha, string s, int val){
    for(int i = alpha; i < 5; i++){
        string temp = s + s1[i];
        int v = val + grade[i];
        c[++idx].val = v;
        c[idx].a = temp;
        f(i+1, temp, v);
    }
}

int main(){
    for(int i = 0; i < 5; i++)     cin >> grade[i];
    //创造序列
    f(0, "", 0);
    sort(c+1, c+idx+1, cmp);
    for(int i = 1; i <= idx; i++)	cout << c[i].a << endl;
    return 0;
}

[ABC384D] Repeated Sequence

题面翻译

给定常数 \(N,S\)\(n\) 个数 \(a_1,a_2,\cdots a_N\),现有无穷项数列 \(A\) 满足

\[A_i=\begin{cases} a_i& 1\le i\le N\\ A_{i-N} & i>N \end{cases} \]

求是否存在两个正整数 \(i<j\) 满足 \(\displaystyle \sum_{k=i}^j A_k=S\)

制約

  • $ 1\leq\ N\leq2\times10\ ^ 5 $
  • $ 1\leq\ A\ _ i\leq\ 10\ ^ 9 $
  • $ 1\leq\ S\leq\ 10\ ^ {18} $
  • 入力はすべて整数

解题思路

无穷项数列\(A\)实际上是一直重复原有数列的值。

满足条件的情况可以简化为:

原序列中的部分序列加上\(x\)倍的\(\displaystyle \sum_{k=i}^n A_k\)(其中\(x\in[0,∞]\)

其中,部分序列有两种情况。

  • 中间连续的一段
  • 开头一段加上结尾一段

因此,可以先让\(s = s\%sum(n)\),再处理剩下的和

对于这两种情况,可以通过开两倍空间,重复原序列,生成新一段的前缀和,合并成一种情况

利用双指针枚举 ,一旦序列中有一段满足要求即输出结果,时间复杂度为\(O(4*n)\)

//abc384d
#include<stdio.h>
#include<iostream>
using namespace std;
const int N = 400010;
typedef long long ll;
ll a[N], sum1[N], sum2[N];

int main(){
    ll n, s;
    cin >> n >> s;
    for(ll i = 1; i <= n; i++){
        cin >> a[i];
        sum1[i] = sum1[i-1] + a[i];
    }
    s = s % sum1[n];
    //前缀和开两倍大,处理后一段的值
    for(ll i = 1; i <= n; i++)  sum1[n+i] = sum1[n] + sum1[i];
    //由中间连续的一段构成(双指针判断)
    ll l = 1, r = 2;
    while(r <= 2 * n){
        while(r <= 2 * n && sum1[r] - sum1[l-1] < s)  r++;
        if(sum1[r] - sum1[l-1] == s && l != r){
            cout << "Yes" << endl;
            return 0;
        }
        l++;
    }
    cout << "No" << endl;
    return 0;
}

[ABC384E] Takahashi is Slime 2

题面翻译

有一个网格,水平行数为 \(H\) ,垂直列数为 \(W\) 。令 \((i, j)\) 表示从上往下第 \(i\)\((1\leq i\leq H)\) 行、从左往右第 \(j\)\((1\leq j\leq W)\) 列的单元格。

最初,单元格 \((i,j)\) 中有一只史莱姆,强度为 \(S _ {i,j}\) ,而 Takahashi 是单元格 \((P,Q)\) 中的史莱姆。

在执行以下任意次数(可能是零次)操作后,找出 Takahashi 的最大可能强度:

  • 在与他相邻的史莱姆中,选择一个强度严格小于他强度的 \(\dfrac{1}{X}\) 倍的史莱姆并吸收它。结果,被吸收的史莱姆消失,而高桥的实力则增加了吸收史莱姆的实力。

执行上述动作时,消失的史莱姆留下的空隙会立即被高桥填补,消失的史莱姆之前相邻的史莱姆(如果有的话)将与高桥重新相邻。

制約

  • $ 1\leq\ H,W\leq500 $
  • $ 1\leq\ P\leq\ H $
  • $ 1\leq\ Q\leq\ W $
  • $ 1\leq\ X\leq10^9 $
  • $ 1\leq\ S\ _\ {i,j}\leq10^{12} $
  • 入力はすべて整数

解题思路

可以发现,史莱姆的实力一定是越来越强。如果要满足\(s[i][j]<power/t\)的条件,那么当\(power\)的值越大时,符合条件的格子越多。

同时,\(s[i][j]\)的值越小,更有可能符合要求。因此在前期应该倾向于先扩散\(s[i][j]\)小的格子,每次在可选的点列表当中选择一个\(s[i][j]\)最小的点,如果满足条件就吸收该格子,并且将四周的格子列入待选列表。

具体代码实现为:

  • 建立结构体存储点的横纵坐标以及该点强度值;
  • 利用优先队列存储能走到的点,小根堆存储能够保证每次队头元素都是强度值最小的;
  • 从起点开始进行bfs扩散,直到所有能够扩散的点都遍历,程序结束。
//abc384e
#include<stdio.h>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;

ll a[505][505];
ll ans = 0;
bool flag[505][505];

struct slime{
    int x, y;
    ll val;
    bool operator < (const slime& a) const{
        return val > a.val;
    }
}s[250010];

//小顶堆
priority_queue<slime> q;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int h, w, t, x, y;

void bfs(){
    while(!q.empty()){
        slime temp = q.top();
        q.pop();
        if(a[temp.x][temp.y] < (ans % t? ans / t + 1: ans / t) || (temp.x == x && temp.y == y)){
            ans += a[temp.x][temp.y];
            for(int i = 0; i < 4; i++){
                int xx = temp.x + dx[i];
                int yy = temp.y + dy[i];
                if(1 <= xx && xx <= h && 1 <= yy && yy <= w && !flag[xx][yy]){
                    q.push({xx, yy, a[xx][yy]});
                    flag[xx][yy] = 1;
                }
            }
        }
    }
}

int main(){
    cin >> h >> w >> t;
    cin >> x >> y;
    for(int i = 1; i <= h; i++)
        for(int j = 1; j <= w; j++)
            cin >> a[i][j];
    q.push({x, y, a[x][y]});
    flag[x][y] = 1;
    bfs();
    cout << ans << endl;
    return 0;
}

[ABC384F] Double Sum 2

题面翻译

定义 \(f(x)=\frac{x}{\text{lowbit}(x)}\)。其中,\(\text{lowbit}(x)\) 表示 \(x\) 二进制中最低位的 \(1\) 所表示的数,可以通过 x&(-x) 求得。

现给定一个长度为 \(n\) 的序列 \(a\),求 \(\sum_{i=1}^{n}\sum_{j=i}^{n}f(a_i+a_j)\)

\(n\le 2\times 10^5,a_i\le 10^7\)

制約

  • $ 1\le\ N\le\ 2\times\ 10^5 $
  • $ 1\le\ A_i\le\ 10^7 $
  • 入力は全て整数

解题思路

如果暴力解决,时间复杂度为\(O(n^2)\)

分析可得到

  • \(lowbit(a_i)!=lowbit(b_i)\)时,\(lowbit(a_i+b_i)=min(lowbit(a_i), lowbit(b_i))\)

先处理出来所有元素的\(lowbit\)值,从大到小排序,构成新序列\(T\)

对于序列中元素\(T_i\)而言,以其值作为分母,对答案的贡献值可以表示成\(({\sum_{idx=1}^{i-1}a_{idx}}+(i-1)*a_i)/T_i\)

其中\(a\)序列以按照lowbit值进行重新排序。(理想状态下,所有元素的lowbit值都不相等的情况下)

  • \(lowbit(a_i)==lowbit(b_i)\)时,需要找到\(lowbit(a_i+b_i)\)值。

\(lowbit(a_i)\)往前,可以发现如果\(a_i\)\(b_i\)的相同位上的数都不一样的话,相加之后都会产生0的结果,因此需要找到从右往左第一位相同位。

需要用到trie树or桶实现该部分问题。

最后把两部分处理的结果加起来即为答案。

posted @ 2025-02-03 13:50  hsy2093  阅读(32)  评论(0)    收藏  举报