A. 宙天

本题主要内容:若存在自然数 k,满足 x=k×(k+1),则称 x 为「终极答案」
由于 x 在0 - 100 之间,直接暴力枚举即可。

代码如下:
#include <iostream>

using namespace std;

int main()
{
    int x;
    cin>>x;
    if(x== 0 || x==2|| x==6 || x==12 || x==20 || x==30 || x==42 || x==56 || x==72 || x==90)
         cout<<"YES"<<endl;
    else  cout <<"NO"<<endl;
    return 0;

}

B. Random

本题主要内容:一个长为 n 的数组, 选择其中两个不同位置的元素,要求这两个元素的最大公约数大于 1。

主要思路:
从头开始将数组的每个元素按质数分解,并存入哈希表中,如果在分解某一元素的时候,刚好出现了这一元素的素数因子之前出现过,就直接退出循环进行输出。

代码如下:
#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N = 2e5 + 100;

int t, n;
int a[N];

int main() 
{
    scanf("%d", &t); 
    
    while (t--) 
    {
        scanf("%d", &n); 
        
        for (int i = 0; i < n; i++) 
            scanf("%d", &a[i]); 
        
        unordered_map<int, int> m;  // 用于存储质因子和对应的数字
        bool res = false;           // 标记是否找到结果
        
        for (int i = 0; i < n; i++) 
        {
            if (a[i] == 1) continue;  // 跳过数字1,因为它没有质因子
            
            int tmp = a[i];
            
            // 处理质因子2
            if (a[i] % 2 == 0) 
            {
                if (m.find(2) != m.end()) 
                {
                    // 如果质因子2已经出现过,输出对应的一对数字
                    printf("%d %d\n", m[2], a[i]);
                    res = true;
                    break;
                } 
                else 
                    m[2] = a[i];     // 否则记录质因子2和当前数字
                
                // 移除所有的因子2
                while (tmp % 2 == 0)
                    tmp /= 2;
            }
            
            // 处理其他奇质因子
            for (int j = 3; j * j <= tmp; j += 2) 
            {
                if (tmp % j == 0) 
                {
                    if (m.find(j) != m.end()) 
                    {
                        // 如果质因子j已经出现过,输出对应的一对数字
                        printf("%d %d\n", m[j], a[i]);
                        res = true;
                        break;
                    } 
                    else
                        m[j] = a[i];    // 否则记录质因子j和当前数字
                    
                    // 移除所有的因子j
                    while (tmp % j == 0) 
                        tmp /= j;
                }
                
                // 如果已经找到结果,提前结束循环
                if (res) break;
            }
            
            // 处理剩余的质因子(大于sqrt(tmp)的质因子)
            if (tmp > 1 && !res) 
            {
                if (m.find(tmp) != m.end()) 
                {
                    // 如果质因子tmp已经出现过,输出对应的一对数字
                    printf("%d %d\n", m[tmp], a[i]);
                    res = true;
                    break;
                } 
                else 
                    m[tmp] = a[i];   // 否则记录质因子tmp和当前数字
            }
            
            // 如果已经找到结果,提前结束循环
            if (res) break;
        }
        
        // 如果没有找到任何符合条件的数字对,输出-1
        if (!res) 
            printf("-1\n");
    }
    
    return 0;
}

C.  Inverted World

本题主要内容: 一个仅由字符 0 和 1 组成的长为 n 的字符串 s ,选择 s 的一个非空子序列 ,该子序列 任意两个相邻元素都不相同,将该子序列进行01反置 ,最少需要多少次操作才能使得 s 中任意两个相邻元素都不相同。

本题主要思路: 目标字符串就是"010101.....010101" 和 "101010.......101010" 这两种,可以把字符串对比两种目标字符串,将与目标字符串一样的字符放入字符串 tmp_0 和 tmp_1 中
将 1 的权值记为 1 ,0 的权值记为 -1,通过找到 tmp_0 和 tmp_1 中的某一段连续子串,计算其 1 所占的优势和 0所占的优势( 即某段子串的权值和 ),而题目中所做的操作,可以让权值差缩减1.
所以我们要找到对应的1 所占的优势的最大值(即权值最大的子串) 和  0所占的优势的最大值(即权值最小的子串),最终二者的绝对值的最大值即为该字符串的答案。
最终比较 tmp_0 和 tmp_1 两者答案的最小值,即为最终答案。

代码如下:
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 2e5 + 100;  

int t, n;  
string s;  

/**
 * 函数 f: 计算将一个字符串变为交替序列所需的最小操作次数
 * 参数 a: 输入字符串,由 '0' 和 '1' 组成
 * 返回值: 最小操作次数
 */
int f(string a) 
{
    int res1 = 0, res2 = 0;  // res1: 最大子段和, res2: 最小子段和
    int now = 0;             // 当前子段和
    
    // 计算最大子段和(将 '1' 视为 +1,'0' 视为 -1)
    for (int i = 0; i < (int)a.size(); i++) 
    {
        if (a[i] == '1') 
            now++;          // 遇到 '1',当前和 +1
        else
            now--;          // 遇到 '0',当前和 -1
        
        if (now < 0)       // 如果当前和小于0,重置为0
            now = 0;
        
        res1 = max(res1, now);  // 更新最大子段和
    }
    
    now = 0;  // 重置当前和为0
    
    // 计算最小子段和
    for (int i = 0; i < (int)a.size(); i++) 
    {
        if (a[i] == '0')
            now--;      
        else
            now++;
        
        if (now > 0)
            now = 0;
        
        res2 = min(res2, now);  
    }
    
    // 返回最大子段和与最小子段和绝对值的较大者
    return max(res1, abs(res2));
}


int main() 
{
    scanf("%d", &t); 
    
    while (t--) 
    {
        scanf("%d", &n);     
        cin >> s;           
        
        string tmp_0 = "", tmp_1 = "";  
        
        // 构建 tmp_0:针对目标模式 0101...(偶数位为0,奇数位为1)
        for (int i = 0; i < (int)s.size(); i++) 
        {
            if (i % 2 == 0 && s[i] == '0') 
                tmp_0 += s[i];
            else if (i % 2 == 1 && s[i] == '1')  
                tmp_0 += s[i];
        }
        
        // 构建 tmp_1:针对目标模式 1010...(偶数位为1,奇数位为0)
        for (int i = 0; i < (int)s.size(); i++) 
        {
            if (i % 2 == 0 && s[i] == '1') 
                tmp_1 += s[i];
            else if (i % 2 == 1 && s[i] == '0') 
                tmp_1 += s[i];
        }
        
        // 计算两种目标模式的最小操作次数,取较小值作为答案
        printf("%d\n", min(f(tmp_0), f(tmp_1)));
    }
    
    return 0;
}










F.  Energy Synergy Matrix

本题主要内容:选择一个当前为空且不为起点的格子放置障碍,使小小红无法进入该格子,并且必须要保证存在通路,求小红到达第 n 列所需的最少步数。

主要思路:
首先明确两个人放置障碍物的作用是什么,小紫放置障碍物的作用是让小红多走一步,即让小红换一行行走,是她多走一步。
小红放置障碍物的作用是:利用必须保证存在通路这个性质,让小紫尽可能后的放置障碍物,使得他在行走到目标列的时候,小紫放的障碍物尽量的少。
由于小红先手,所以每个人的最优放置如下所示:       小红的障碍物用:红  表示,小紫的用:紫   表示
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15























小紫放置的障碍物只会出现在5, 10 , 15......的位置,并且小紫的障碍物出现一次,就会让小红多走一步。
因此小红走的步数为  n - 1 + n /5  

代码如下:
#include <iostream>
#include <algorithm>

using namespace std;

int t,n;

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("%d\n",n-1+n/5);
    }
    return 0;
}

G.  スピカの天秤

本题主要内容: 求解最少拿走多少块砝码,可以更改天平的状态。

主要思路:
左侧的重量等于右侧的时候,只要拿走任意一块,天平的状态一定会改变。
左侧的重量等于右侧的时候,要想改变天平状态,肯定需要从重的一侧拿,直到重的一侧变得与另一侧相等或者比另一侧轻。那么为了拿的块数更少,就要尽可能的拿最重的砝码,这样才能保证拿的块数最少。

代码如下:
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 100;

int t, n, m;              
int a[N], b[N]; 

int main() 
{
    scanf("%d", &t);
    
    while (t--) 
    { 
        scanf("%d %d", &n, &m); 
        
        long long sum1 = 0, sum2 = 0;  // 用于存储两个数组的总和
        
        for (int i = 0; i < n; i++) 
        {
            scanf("%d", &a[i]);
            sum1 += a[i];
        }
        
        for (int i = 0; i < m; i++) 
        {
            scanf("%d", &b[i]);
            sum2 += b[i];
        }
        
        // 如果两个数组的总和相等,直接输出1
        if (sum1 == sum2) 
        {
            printf("1\n");
            continue;
        }
        
        // 如果数组a的总和大于数组b的总和
        if (sum1 > sum2) 
        {
            sort(a, a + n);  // 对数组a进行升序排序
            
            int cnt = 0;  // 计数器,记录需要移出的砝码个数
            
            // 不断从数组a中移出最大的元素,直到总和不再大于sum2
            while (sum1 > sum2) 
            {
                sum1 -= a[n - 1 - cnt];  // 减去最大的元素
                cnt++;              
            }
            
            printf("%d\n", cnt);
        } 
        // 如果数组b的总和大于数组a的总和
        else 
        {
            sort(b, b + m);  // 对数组b进行升序排序
            
            int cnt = 0; 
            
            // 不断从数组b中移出最大的元素,直到总和不再大于sum1
            while (sum1 < sum2) 
            {
                sum2 -= b[m - 1 - cnt];  // 减去最大的元素
                cnt++;              
            }
            
            printf("%d\n", cnt);
        }
    }
    
    return 0;
}

H.  Tic Tac DREAMIN’

本题主要内容:在 x 轴上寻找一个锚点 O(x,0),使得以 A,B,O 为顶点的三角形面积恰好等于 2
主要思路:在坐标系上的三角形面积公式:\frac{1}{2}|(Xa - X) * Yb - (Xb - X) * Ya| = S
整理得到公式:| XaYb - XbYa + (Ya - Yb )X | = 4    这个方程的 X 的解即为所求的答案。

这里需要了解:  当Ya - Yb相等的时候,x是可以取任何值的,但必须要保证 |  XaYb - XbYa | = 4 成立, 否则无解。
Ya - Yb不相等的时候,只需要求解   XaYb - XbYa + (Ya - Yb )X = 4 的X的取值即为答案。(因为题目说输出一个答案即可,所以可以直接去掉绝对值)。 

代码如下:
#include <iostream>
#include <algorithm>

using namespace std;
const int N= 2e5 + 100;

double xa ,xb ,ya ,yb;

int main()
{
    scanf("%lf %lf",&xa, &ya);
    scanf("%lf %lf",&xb, &yb);
    if( abs(ya - yb) < 1e-9)
    {
        if( abs( abs( xa * yb - xb * ya) - 4) < 1e-9)
           printf("0");
        else printf("no answer");
    }
    else 
       printf("%.8lf",(4 - xa * yb + xb * ya) / (ya - yb));
    
    return 0;
}




J.  Branch of Faith

本题主要题意:  一棵包含 nn 个节点的完全二叉树,输出与 x 号节点深度相同的节点有多少个



根据完全二叉树的性质我们可以得知:
深度为0时 ,即根节点 1 ,就是区间[ 20 , 21 - 1 ]
深度为1时, 即节点 2 , 3,  就是区间  [ 21 , 22 - 1 ]
深度为2时, 即节点 4 ,5,6, 7  就是区间  [ 22 , 23  - 1 ] 
因此 我们可以得知,当节点 k 位于 [ 2n ,  2n+1-1 ] 的区间时, 深度相同的节点有 2n+1-1 - 2+ 1 个

需要注意的是:当深度是最深的时候,未必会将最后一层全部填满,所以需要特判一下 n 和 2n+1-1 的大小。

代码如下:
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 2e5 + 100; 

int t, q;                 
long long n;              

// 函数f: 根据给定的n和x计算某个值
// 参数: n - 限制值, x - 查询值
long long f(long long n, long long x) 
{
    // 当x为1时,直接返回1
    if (x == 1) return 1;
    
    int idx = 2;  // 从idx=2开始寻找合适的指数
    
    // 找到满足条件的idx,使得x在[2^(idx-1), 2^idx-1]区间内
    while (x > ((1LL << idx) - 1) || x < (1LL << (idx - 1)))
        idx++;
    
    // 计算结果: min(n, 2^idx-1) - 2^(idx-1) + 1
    return min(n, ((1LL << idx) - 1)) - (1LL << (idx - 1)) + 1;
}

int main() 
{
    scanf("%d", &t); 
    
    while (t--) 
    {  
        scanf("%lld %d", &n, &q);  
        
        for (int i = 0; i < q; i++) 
        { 
            long long x;
            scanf("%lld", &x);        
            printf("%lld\n", f(n, x)); 
        }
    }
    
    return 0;
}