A.Exponential Plant(模拟)
题意
有一棵植物,从第000天起每天结束后会长高2i2^{i}2i厘米,问:第几天开始时植物的高度会超过给出的身高。
分析
循环累加直到超过身高即可。
Hint:注意当给出身高较大时,植物高度可能在累加中超过int范围,需要使用long long类型存储。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5e2;
int n, m, k, a[N];
void solve() {
cin >> n;
ll h = 0;
for (int i = 0; ; i++) {
h += (1ll << i);
if (h > n) {
cout << i + 1 << endl;
return;
}
}
}
int main () {
solve();
return 0;
}
B.AtCoder Janken 2(排序)
题意
有NNN个人参加了一场比赛,每个人有着自己的名字以及获得的分数。
你需要按照以下规则计算出最后的胜利者:
-
将这NNN个人按照名字的字典序进行排序
-
计算这NNN个人的分数总和TTT,并让TTT对NNN取模,让TTT%NNN作为下标,排序好的所有人中下标为TTT%NNN的即为最后的胜利者
分析
由于分数只是用来计算最后的胜利者,因此可以不用和名字一起放在结构体中,可以单独存储。
字符串排序默认就是按字典序排序的,因此可以直接对字符串数组进行sortsortsort。
统计完分数总和以及将名字排序后,以分数总和取模后的结果作为下标输出胜利者的名字即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5e2;
int n, m, k, a[N];
string s[N];
void solve() {
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> s[i] >> a[i];
sum += a[i];
}
sort(s + 1, s + n + 1);
cout << s[sum % n + 1] << endl;
}
int main () {
solve();
return 0;
}
C.AtCoder Magics(双指针+multiset)
题意
给出NNN张卡牌,每张卡牌均包含两个属性:力量AiA_iAi以及花费CiC_iCi。
你可以执行以下操作若干次直到无法进行操作:
- 选择两张卡牌x,yx, yx,y,且满足Ax>AyA_x > A_yAx>Ay and Cx<CyC_x < C_yCx<Cy,将卡牌yyy丢掉。
问:最后留下来了哪些卡牌,输出这些卡片的编号。
分析
首先可以对卡牌按AiA_iAi从小到大排序,那么排完序后,只要有i<ji < ji<j,必有Ai≤AjA_i \le A_jAi≤Aj,即所有能淘汰当前卡牌的牌下标必须大于当前卡牌,可以通过upper_bound查找第一个大于等于Ai+1A_{i} + 1Ai+1的卡牌出现的位置。
但是,可以发现,虽然后面的卡牌的力量均大于等于当前卡牌,但是此时所有卡牌的花费依然是无序的,没有办法解决花费的问题。
这里可以使用multisetmultisetmultiset(不去重的setsetset)维护所有力量大于当前卡牌的牌的花费,每次遍历到一张卡牌时,使用另一个指针将所有力量与当前的卡牌相同的牌的花费从set中删除。
这样,setsetset中所有元素对应卡牌的力量都是比当前卡牌更大的,那么只要存在其中一张的花费比当前卡牌小,当前卡牌就会被删除,即,只有当前卡牌的花费比后面所有牌的花费均小(小于等于),当前卡牌就不会被删除,由于multisetmultisetmultiset也会排序,那么只需要与multisetmultisetmultiset存储的第一个元素比较即可,如果当前卡牌的花费小于等于存储的最小的花费,那么当前卡牌就会被保留。
由于需要排序,那么会打乱卡牌的编号,可以使用结构体一起记录卡牌的编号,卡牌将被删除仅使用vector记录卡牌编号,最后将编号排序输出即可。
hint
如果掌握线段树等解决区间极值的算法,不难想到,可以对排序后卡牌的花费维护区间最小值,每次使用二分找到第一个力量大于当前卡牌的卡牌所在的下标,然后对这个下标到nnn查询区间最小值,如果查询到的结果要大于等于当前卡牌的花费,则当前卡牌就不会被淘汰。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5e2;
struct Node{
int a, c, id;
bool operator < (const Node &o) const {
return a < o.a;
}
}a[N];
int n;
multiset<int> st;
vector<int> ans;
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].a >> a[i].c;
a[i].id = i;
st.insert(a[i].c);
}
sort(a + 1, a + n + 1);
int j = 1;
for (int i = 1; i <= n; i++) {
while (j <= n && a[i].a == a[j].a) {
st.erase(a[j++].c);
}
if (st.empty() || *st.begin() >= a[i].c) {
ans.push_back(a[i].id);
}
}
sort(ans.begin(), ans.end());
cout << ans.size() << endl;
for (auto i : ans) cout << i << ' ';
}
int main () {
solve();
return 0;
}
D.AtCoder Wallpaper(思维)
题意
给出一个二维坐标系,二维坐标系内的元素如下图所示:

给出两个坐标(A,B),(C,D)(A, B), (C, D)(A,B),(C,D),问以(A,B)(A, B)(A,B)为左下角(C,D)(C, D)(C,D)为右上角的矩形中,包含多少个黑色的三角形?
分析
如下图红框内所示,实际上图形是以一个2×42 \times 42×4的矩形循环摆放的:

因此,对于矩形内的黑色三角形数量,可以将问题转化为区间内,2×42 \times 42×4的矩形中每一个小网格的出现次数,再乘上这个小网格包含的黑色三角形数量,就是这个小网格产生的贡献。
每一个小网格的出现次数可以通过横坐标上出现次数乘上列坐标上出现次数计算得到。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5e2;
ll p[2][4] = {{2, 1, 0, 1},
{1, 2, 1, 0}};
const ll maxn = 1e9 + 4;//随意取一个大整数,需要保证足够大且是4的倍数
void solve() {
ll a, b, c, d;
cin >> a >> b >> c >> d;
ll ans = 0;
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 4; x++) {
ll sum_x = (c - x + 3 + maxn) / 4 - (a - x + 3 + maxn) / 4,
sum_y = (d - y + 1 + maxn) / 2 - (b - y + 1 + maxn) / 2;
ans += sum_x * sum_y * p[y][x];
}
}
cout << ans << endl;
}
int main() {
solve();
return 0;
}
E.Remove Pairs(dp)
题意:
小AAA和小BBB正在玩一个使用 NNN 张卡片的游戏。 第iii张牌的正面写着 AiA_iAi ,背面写着 BiB_iBi 。最初, NNN 这张牌摆在桌子上。小AAA先出,两位玩家轮流进行以下操作:
- 从桌上选择一对正面数字相同或背面数字相同的牌,然后从桌上拿走这两张牌。如果没有这样的一对牌,玩家就不能进行操作。
最先无法进行操作的玩家输,另一名玩家赢。如果双方都以最佳方式出牌,谁会赢?
分析:
dp[i]dp[i]dp[i]表示用一个二进制数来代表现在还有的卡牌状况。先手是必胜还是必输。当前转移是必胜还是必输,要看后继状态是否存在必输态。如果存在必输态,则当前状态iii可以通过对应的转移变成先手必输态jjj,那么说明当前状态iii是必胜,dp[i]=1dp[i]=1dp[i]=1,否则如果所有后继状态都是必胜态,则说明当前是必输态,dp[i]=0dp[i]=0dp[i]=0。而后继状态就是当前状态可以做的决策的转移,即选择两张正面数字一样或反面数字一样的卡牌拿走。我们可以n2n^2n2枚举选择哪两张牌即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using namespace std;
int main() {
int n;
cin >> n;
vector<array<int, 2>> tmp(n);
for (auto &v: tmp)
cin >> v[0] >> v[1];
int num = (1 << n);
vector<int> dp(num, -1);
dp[0] = 0;
auto dfs = [&](auto dfs, int s) -> int {
if (dp[s] != -1)
return dp[s];
int flag = 0;
for (int i = 0; i < n; i++) {
if ((~s >> i) & 1)
continue;
for (int j = i + 1; j < n; j++) {
if ((~s >> j) & 1)
continue;
if (tmp[i][0] != tmp[j][0] && tmp[i][1] != tmp[j][1])
continue;
flag |= !dfs(dfs, s ^ (1 << i) ^ (1 << j));
}
}
return dp[s] = flag;
};
int flag = dfs(dfs, num - 1);
if (flag) {
cout << "Takahashi" << endl;
} else {
cout << "Aoki" << endl;
}
return 0;
}
F.Useless for LIS(树状数组)
题意:
问题陈述
给你一个长度为NNN的整数序列AAA。
对于每个t=1,2,…,Nt=1,2,\dots,Nt=1,2,…,N,判断AtA_tAt是否包含在AAA的最长递增子序列中。
这里,只有当且仅当下面的条件成立时,AtA_tAt才包含在AAA的最长递增子序列中:
-
设LLL是AAA的最长递增子序列的长度。存在一个严格递增整数序列i=(i1,i2,…,iL)(i1<i2<⋯<iL)i=(i_1,i_2,\dots,i_L)(i_1\lt i_2\lt \dots\lt i_L)i=(i1,i2,…,iL)(i1<i2<⋯<iL),其中每个元素都介于111与NNN之间(包括首尾两个元素),且满足以下所有条件:
- Ai1<Ai2<⋯<AiLA_{i_1}\lt A_{i_2}\lt\dots\lt A_{i_L}Ai1<Ai2<⋯<AiL。
- ik=ti_k=tik=t为某个k(1≤k≤L)k(1\leq k\leq L)k(1≤k≤L)。
给你TTT个测试样例,逐个求解。
什么是最长递增子序列?
序列AAA的子序列是指从AAA中提取一些元素而不改变顺序所得到的序列。
序列AAA的最长递增子序列是AAA的子序列,它以最大可能的长度严格递增。
分析:
用树状数组求最长上升子序列长度。
本题aia_iai比较大,上树之前先进行离散化处理。
定义did_idi为以aia_iai结尾的最长上升子序列的长度。
根据树状数组对最长上升子序列长度的计算方法可以得出一个结论:
- 对于所有满足dx≥2d_x \ge 2dx≥2的整数xxx,必然存在整数
yyy满足x<yx\lt yx<y且dy+1=dxd_y+1=d_xdy+1=dx。
也就是说,我们可以从后往前枚举yyy,寻找符合条件的xxx,若xxx存在,这个下标就是答案之一。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod=1000000007;
const int N=2e5+10;
int n, a[N], b[N], m, tr[N], dp[N], req[N], res;
list<int> ret;
inline void update(int x, int v){
for (; x <= m; x += (x & -x))
tr[x] = max(tr[x], v);
}
inline int query(int x){
int res = 0;
for (; x; x -= (x & -x))
res = max(res, tr[x]);
return res;
}
void solve(){
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - b - 1;
memset(tr + 1, 0, m << 2);
memset(req + 1, 0, m << 2);
for (int i = 1; i <= n; i++){
a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
}
res = 0;
for (int i = 1; i <= n; i++){
dp[i] = query(a[i] - 1) + 1;
res = max(res, dp[i]);
update(a[i], dp[i]);
}
ret.clear();
req[res] = 0x3f3f3f3f;
for (int i = n; i; i--){
if (req[dp[i]] > a[i])
ret.push_front(i), req[dp[i] - 1] = max(req[dp[i] - 1], a[i]);
}
cout << ret.size() << endl;
list<int>::iterator it;
for (it = ret.begin(); it != ret.end(); ++it) {
cout << *it << ' ';
}
cout << endl;
}
int main(){
int T;
cin >> T;
while (T--)
solve();
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

793

被折叠的 条评论
为什么被折叠?



