The Festival of |

hsy2093

园龄:1年4个月粉丝:0关注:0

ABC384题解

[ABC384C] Perfect Standings

题面翻译

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

竞赛包含五道题目:A、B、C、D、E,得分分别为 abcde

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

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

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

输入格式

a b c d e

输出格式

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

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

制約

  • 100 a b c d e 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,Sn 个数 a1,a2,aN,现有无穷项数列 A 满足

Ai={ai1iNAiNi>N

求是否存在两个正整数 i<j 满足 k=ijAk=S

制約

  • 1 N2×10 5
  • 1 A i 10 9
  • 1 S 10 18
  • 入力はすべて整数

解题思路

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

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

原序列中的部分序列加上x倍的k=inAk(其中x[0,]

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

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

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

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

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

//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(1iH) 行、从左往右第 j(1jW) 列的单元格。

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

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

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

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

制約

  • 1 H,W500
  • 1 P H
  • 1 Q W
  • 1 X109
  • 1 S  i,j1012
  • 入力はすべて整数

解题思路

可以发现,史莱姆的实力一定是越来越强。如果要满足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)=xlowbit(x)。其中,lowbit(x) 表示 x 二进制中最低位的 1 所表示的数,可以通过 x&(-x) 求得。

现给定一个长度为 n 的序列 a,求 i=1nj=inf(ai+aj)

n2×105,ai107

制約

  • 1 N 2× 105
  • 1 Ai 107
  • 入力は全て整数

解题思路

如果暴力解决,时间复杂度为O(n2)

分析可得到

  • lowbit(ai)!=lowbit(bi)时,lowbit(ai+bi)=min(lowbit(ai),lowbit(bi))

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

对于序列中元素Ti而言,以其值作为分母,对答案的贡献值可以表示成(idx=1i1aidx+(i1)ai)/Ti

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

  • lowbit(ai)==lowbit(bi)时,需要找到lowbit(ai+bi)值。

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

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

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

本文作者:hsy2093

本文链接:https://www.cnblogs.com/hsy2093/p/18697455

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hsy2093  阅读(8)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起