I. In Search of the Ultimate Artifact
分析:签到,要注意我们需要排序,再找到最先为0的位置这个k -1大小的区间就不能选了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
// 0 0 1 2 3 4 5 6
void solve()
{
int n, k;
cin >> n >> k;
vector<ll> a(n);
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a.begin(), a.end(), greater<int>());
int d0 = n;
ll ans = a[0] % mod;
for (int i = 0; i < n; i++)
{
if (a[i] == 0)
{
d0 = i;
break;
}
}
// 每次可以找 k -1个 看一共可以找多少个 k - 1
// cout << "sum * ( k - 1) = " << endl;
int sum = (d0 - 1) / (k - 1);
for (int i = 1; i <= sum * (k - 1); i++)
{
ans = ans * a[i] % mod;
}
cout << ans << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
solve();
return 0;
}
C. Conquer the Multiples
分析:注意到拿奇数的人可能去阻断拿偶数的人,而偶数的人不可能阻断自己,分类一下下就可以
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int l, r;
cin >> l >> r;
// l l + 1 l + 2 , ...
// l l + 1 l + 2 l + 3
// 最先阻断对方的是胜利者
// 偶数的一方不会去跳跃拿
// 奇数的一方会跳跃拿 跳跃 2倍 或者跳跃4倍 偶数倍 但实际上只要能跳偶数这个人就必输
if (l % 2 == 1)
{
if (l * 2 <= r)
{
cout << "Alice" << endl;
}
else
{
if (r % 2 == 1)
cout << "Alice" << endl;
else
cout << "Bob" << endl;
}
}
else
{
if ((l + 1) * 2 <= r - (r % 2))
{
cout << "Bob" << endl;
}
else
{
if (r % 2 == 1)
cout << "Bob" << endl;
else
cout << "Alice" << endl;
}
}
}
return 0;
}
B. Basic Graph Algorithm
分析:手速可以铜牌的题,本身不难,但是要注意很多细节,以及必须按照dfs的顺序进行,不能手动模拟。
#include <bits/stdc++.h>
using namespace std;
int n, m;
int arr[300005], nex = 1;
vector<int> vct[300005];
set<int> st[300005];
vector<pair<int, int>> ans;
void dfs(int u)
{
// 这里会有一个非常非常不好发现的错误!!如果没有vct再存一次图,只是用set存图的话.
// 一旦x在y前先遍历,那么y会删除到x这条边,但是x不会删除到y这条边(因为y到x已经没边了).
// 如果此时y不是被x遍历到的,那么将可能会多建边.
// 即x应该要return才对的,但是x没有return,因为还存有到y的边,但是实际上y已经被遍历完了.
for (auto v : vct[u])
st[v].erase(u); // 因为这个点已经遍历过了 所有想走到它的边都必须删除
while (!st[u].empty() && nex <= n)
{
if (st[u].count(arr[nex]))
{
// st[u].erase(arr[nex]);
nex++, dfs(arr[nex - 1]);
}
else
{
ans.emplace_back(u, arr[nex]);
nex++, dfs(arr[nex - 1]);
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
// 无自环,有重边.
// 需要再建一个不做删减的图,用作给st删边.如果单纯用st作为删边的话,会有问题!!并且很难发现这个问题..
/*if(!st[u].count(v))*/
vct[u].emplace_back(v), vct[v].emplace_back(u);
st[u].emplace(v), st[v].emplace(u);
}
for (int i = 1; i <= n; i++)
cin >> arr[i];
while (nex <= n)
nex++, dfs(arr[nex - 1]);
cout << ans.size() << endl;
for (auto a : ans)
cout << a.first << " " << a.second << endl;
return 0;
}
G. Geometry Task
分析:根据数据范围注意到是一个二分的题目,但是要贪心进行选蓝的坐标,我们可以找到满足m的需要的x坐标,对于正斜率的和负斜率的分别存起来,排序之后再贪心的选择,负斜率的x坐标从小到大排序,可以实现先满足苛刻的负斜率,把蓝的坐标作用最大化,正斜率的x坐标从大到小排序,可以实现先满足更苛刻的正斜率(因为rx是从右边开始移动的),最后我们判断满足的数量 return num >= (n + 1) / 2;。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ld long double
const int N = 1e5 + 10 , INF = 2e18;
struct{
int a, b;
} l[N];
void solve(){
int n;
cin >> n ;
for (int i = 0; i < n; i++ )
cin >> l[i].a;
for (int i = 0; i< n ; i ++)
cin >> l[i].b;
vector<int> c(n);
for (int i = 0; i < n;i ++ )
cin >> c[i];
sort(c.begin(), c.end());
auto check = [&](ld m) -> bool
{
int num = 0; // 满足要求的线的数量 如果是水平的就得直接去判断可不可以满足要求
vector<int> lt, rt;//分别存正斜率和负斜率需要的x坐标的界限
for (int i = 0; i<n ; i ++)
{
ld a = l[i].a, b = l[i].b;
if(a == 0 ){
if(l[i].b >= m)
num++;
continue;
}
if( a > 0 ){
// 一个正斜率的线要满足大于m x坐标至少是 (m - b) / a 上取整
int nd_x = ceil((m - b ) / a);
rt.push_back(nd_x);
}
if( a < 0){
// 一个负斜率的线要满足大于m x的坐标至多是 ( m - b ) / a下取整 负数除法
int nd_x = floor((ld)(m - b) / (a));
lt.push_back(nd_x);
}
}
// 根据搜索方向不同 排序的方向也不同
sort(lt.begin(), lt.end());
sort(rt.begin(), rt.end(), greater<int>());
int lx = 0, rx = n - 1; // 分别是开始搜索的方向
vector<int> vis(n, 0);
for (auto &i : lt)
{
// 先满足小的
if (c[lx] <= i && lx < n )
{
vis[lx] = 1;
lx++;
num++;
}
}
for (auto &i : rt)
{
if (c[rx] >= i && !vis[rx] && rx >= 0)
{
num++;
rx--;
}
}
return num >= (n + 1) / 2;
};
int l = -INF, r = INF;
while(l <= r){
int mid = l + r >> 1;
if(check(mid)) l = mid + 1;
else
r = mid - 1;
}
cout << r << endl;
}
signed main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while( t -- )
solve();
return 0;
}
D. Decrease and Swap
分析:这个题目就很头大了,如果运气好注意到我们只关心最后两位能不能 消掉,就可以尽可能的去把1匀到右边去,比如1111000101就可以匀成1010101110,然后去判断后五位的情况:
-
具体情况分析 (Case Analysis):
-
倒数三、四位是
11(...11xx): 这种情况总是有解的。因为s[n-4]和s[n-3]的 '1' 提供了足够多的交换机会,可以灵活地处理s[n-2]和s[n-1]的情况,最终都能把它们变成 '0'。 -
倒数三、四位是
00(...00xx): 这种情况总是无解的。因为缺少了s[n-4]和s[n-3]的操作能力,末尾的 '1' 会被“锁死”。 -
倒数三、四位是
10(...10xx): 只能解决一些特殊情况。s[n-4]的 '1' 提供了一次交换s[n-3]和s[n-2]的机会,能否成功取决于s[n-2]和s[n-1]的具体值。 -
倒数三、四位是
01(...01xx): 能否解决取决于倒数第五位。如果倒数第五位是 '1',可以通过操作将其变为...10的情况,从而可能解决问题;如果倒数第五位是 '0',则无法操作,问题无解。
-
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
const int N = 1e6 + 10;
void solve()
{
int n;
cin >> n;
vector<char> s(n);
for (auto &i : s)
cin >> i;
if (s[n - 1] == '0' && s[n - 2] == '0')
{
cout << "Yes" << endl;
return;
}
if (n == 3)
{
cout << "No" << endl;
return;
}
for (int l = 0; l < n - 2; l++)
{
if (s[l] != '1')
continue;
int cnt = 1;
int r = l + 1; // [l , r) 这样的一个区间尽可能去扩展
while (r < n - 1 && (r - l) <= 2 * cnt)
{
if (s[r] == '1')
cnt++;
r++;
}
// 先全部清空
for (int i = l; i < r; i++)
s[i] = '0';
for (int i = l; i < r; i+=2)
{
s[i] = '1';
cnt--;
if(cnt == 0 )
break;
}
// 最后的cnt可能仍然有剩余
for (int i = r - 1; i >= l; i--)
{
if (cnt == 0)
break;
if (s[i] == '0')
{
s[i] = '1';
cnt--;
}
}
l = r - 1;
}
if (s[n - 2] == ' 0' && s[n - 1] == '0' || s[n - 4] == '1' && s[n - 3] == '1')
cout << "Yes" << endl;
else if (s[n - 4] == '0' && s[n - 3] == '0')
cout << "No" << endl;
else if (s[n - 4] == '1' && s[n - 3] == '0')
{
if (s[n - 2] == '0' && s[n - 1] == '1')
cout << "No" << endl;
else
cout << "Yes" << endl;
}
else if (n >= 5 && s[n - 5] == '1')
cout << "Yes" << endl;
else
cout << "No" << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
906

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



