AtCoder 第409场初级竞赛 A~E题解

A Conflict

【题目链接】

原题链接:A - Conflict

【考点】

枚举

【题目大意】

找到是否有两人都想要的物品。

【解析】

遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。

【难度】

GESP三级

【代码参考】

#include
using namespace std; 

int main(){  
	string t, a;
	int n;
	cin >> n;
	cin >> t >> a;
	for(int i = 0; i < n; i++){
		if(t[i] == 'o' && a[i] == 'o'){
			cout << "Yes";
			return 0;
		} 
	} 
	cout << "No";
	return 0;
}

B Citation

【题目链接】

原题链接:B - Citation

【考点】

枚举

【题目大意】

这道题要求找到最大的非负整数 x,使得序列中至少有 x 个元素大于或等于 x

【解析】

利用排序和贪心策略来高效地确定这个 x 的值。

  • 排序:将序列按降序排列,这样可以方便地从大到小检查每个可能的 x 值。
  • 遍历检查:对于每个可能的 x 值(从 1 到 N),检查排序后的第 x 个元素(索引为 x-1)是否大于或等于 x。如果满足条件,则 x 是一个可行解。
  • 确定最大值:遍历所有可能的 x 值后,最后一个满足条件的 x 即为所求的最大值。

【难度】

GESP三级

【代码参考】

#include
using namespace std; 

int main(){  
	int n, a[105];
	cin >> n;
	for(int i = 0; i < n; i++){
		cin >> a[i];
   	}
    // 按降序排列数组
	sort(a, a + n, greater());
	int maxn = 0;
	for(int i = 1; i <= n; i++){
        // 检查第i个元素(索引为i-1)是否大于或等于i
		if(a[i-1] >= i) maxn = i;
	}
	cout << maxn;
	return 0;
}

C Equilateral Triangle

【题目链接】

原题链接:C - Equilateral Triangle

【考点】

数组计数法,三元组

【题目大意】

这道题要求在周长为 L 的圆上找到等边三角形的三元组 (a,b,c),其中每个点之间的弧长相等。

【解析】

关键在于利用圆的周期性和等边三角形的特性来高效计算符合条件的三元组数量。

  • 条件判断:首先检查圆的周长 L 是否能被 3 整除。如果不能,则无法形成等边三角形,直接返回 0。
  • 点的位置计算:通过前缀和数组计算每个点在圆上的位置,并对 L 取模,确保位置在 [0, L-1] 范围内。
  • 数组统计:使用数组统计每个位置出现的次数。
  • 三元组计数:如果 L 能被 3 整除,将圆分成三等份。对于每个可能的起点 i(范围从 0 到 L/3-1),检查 i、i+L/3 和 i+2*L/3 这三个位置的点数量。符合条件的三元组数目为这三个位置点数量的乘积之和。

【难度】

GESP四级

【代码参考】

#include
using namespace std; 

const int N = 3e5 + 5;
long long a[N], n, l;  // a数组用于统计每个位置出现的次数

int main(){  
    cin >> n >> l;
    int x = 0;
    a[x] = 1;  // 初始点1的位置为0
    for(int i = 0; i < n-1; i++){
        int t;
        cin >> t;
        x = (x + t) % l;  // 计算下一个点的位置
        a[x]++;  // 统计该位置出现的次数
    } 
    
    // 如果周长不能被3整除,无法形成等边三角形
    if(l % 3 != 0){
        cout << 0;
        return 0;
    }
    
    long long ans = 0;
    // 遍历每个可能的起点i
    for(int i = 0; i < l / 3; i++){
        // 计算三个等距位置的点数量乘积,并累加到答案中
        ans += (a[i] * a[i + l / 3] * a[i + l / 3 * 2]);
    }
    cout << ans;
    return 0;
}

D String Rotation 

【题目链接】

原题链接:D - String Rotation

【考点】

字符串

【题目大意】         

这道题要求对字符串执行一次循环左移操作,使得结果字符串的字典序最小。

【解析】

关键在于找到最优的子串位置进行操作,从而最小化字典序。

  • 寻找第一个递减位置:从左到右遍历字符串,找到第一个满足 s[i] > s[i+1] 的位置 p。这个位置是字典序可能减小的关键点。

  • 处理全递增情况:如果字符串完全递增(即不存在上述位置 p),则直接输出原字符串(因为任何操作都无法减小字典序)。

  • 确定最优子串:从位置 p+1 开始向右遍历,找到最大的连续子串,使得子串中的每个字符都小于或等于 s[p]。这个子串就是需要循环左移的部分。

  • 构造结果字符串:将子串的第一个字符移到子串末尾,然后按顺序输出其余部分。

【难度】

GESP五级

【代码参考】

#include
using namespace std; 

int main(){  
    string s;
    int t;
    cin >> t;
    while(t--){
        int i, p, n;
        cin >> n >> s;
        
        // 寻找第一个递减位置p
        for(i = 0; i < n-1; i++){
            if(s[i] > s[i+1]) break;
            cout << s[i];  // 输出递增部分
        }
        p = i;
        
        // 如果字符串完全递增,直接输出并处理下一个测试用例
        if(p == n-1){
            cout << endl;
            continue;
        } 
        
        // 寻找从p+1开始的最大连续子串,其中每个字符都<=s[p]
        for(i = p + 1; i < n; i++){
            if(s[i] > s[p]) break;
            else cout << s[i];  // 输出子串中<=s[p]的部分
        }
        
        // 输出被移到最后的字符s[p]
        cout << s[p];
        
        // 输出剩余部分
        for(; i < n; i++){
            cout << s[i];
        }
        cout << endl;
    }
    return 0;
}

E Pair Annihilation

【题目链接】

原题链接:E - Pair Annihilation

【考点】

深度优先搜索dfs,树

【题目大意】

这道题要求在树结构中计算正电子和电子湮灭的最小能量。

【解析】

关键在于利用树的特性,通过后序遍历(DFS)从叶子节点向根节点合并粒子,确保每个粒子的移动路径最短,从而最小化总能量消耗。

  • 树的特性:树是无环连通图,粒子只能通过边在节点间移动,最短路径唯一。
  • 后序遍历合并粒子:从叶子节点开始,将每个子树的粒子数合并到父节点。粒子移动时,消耗的能量为边权乘以粒子数的绝对值(绝对值代表粒子数量,正负代表类型)。
  • 能量累加:在遍历过程中,累加每条边因移动粒子而消耗的能量。由于总和为零,最终根节点的粒子数必为零,所有粒子在移动过程中湮灭。

【难度】

GESP六级

【代码参考】

#include
using namespace std; 

const int N = 1e5 + 5;
struct node{
    int v;
    long long w;
}; 
long long ans, a[N]; // ans存储总能量,a数组存储各节点粒子数
int n, u;
vector tree[N]; // 邻接表存储树结构

// 深度优先搜索,fa为父节点,x为当前节点
void dfs(int fa, int x){
    for(auto v : tree[x]){ // 遍历当前节点的所有邻接节点
        if(v.v == fa) continue; // 跳过父节点,避免回退
        dfs(x, v.v); // 递归处理子节点
        // 累加移动子节点v.v的粒子到当前节点x的能量
        ans += v.w * abs(a[v.v]);
        // 合并子节点的粒子数到当前节点
        a[x] += a[v.v];
    }
}

int main(){  
    cin >> n;
    for(int i = 0; i < n; i++) cin >> a[i];
    for(int i = 1; i < n; i++){
        node vl;
        cin >> u >> vl.v >> vl.w;
        u--, vl.v--;
        tree[u].push_back(vl); // 添加双向边
        swap(u, vl.v); // 交换顶点,添加反向边
        tree[u].push_back(vl);
    }
    dfs(-1, 0); // 从根节点0开始遍历,父节点设为-1(无效值)
    cout << ans; // 输出最小总能量
    return 0;
}

你可能感兴趣的:(青少年编程比赛题解,算法,AtCoder,c++,深度优先)