
壁纸链接
题目链接:https://atcoder.jp/contests/abc474/tasks
A - Not X
只需要构造一个属于且不等于的整数。若就输出,否则输出,两种情况都一定满足要求。时间复杂度和空间复杂度均为。
void solve(){ int x; cin >> x; if(x == 2) cout << 1 << '\n'; if(x == 3) cout << 1 << '\n'; if(x == 1) cout << 2 << '\n';}B - Exit Order
按照编号每个元素划分一组,第个位置应当由第组中的元素占据。该组允许出现的最大编号为 。 因此依次检查是否超过这个上界即可。虽然代码没有显式检查当前组的下界,但是一个排列:前面的完整位置组已经用完了所有更小编号,所以当前元素不可能属于更早的组。若所有元素均未超过对应上界,分组顺序就是合法的。时间复杂度为,额外空间复杂度为。
void solve(){ int n; cin >> n; bool f = true; for(int i = 1; i <= n;i++){ int x; cin >> x; int val = ((i - 1) / 10 + 1) * 10; if(x > val) f = false; } if(f) cout << "Yes" << '\n'; else cout << "No" << '\n';}C - Remove and Append
直接在数组中删除元素会引起整体移动,单次操作最坏需要。由于是到的排列,可以直接以元素值作为下标,用和分别记录的前驱和后继,从而用数组模拟双向链表;同时维护链表的和。 将移动到末尾时,先用它的前驱和后继相互连接。若原本是头节点,则改为更新;然后令原来的指向,并将设为新的尾节点。若已经位于末尾,序列不会改变,可以直接跳过。每次修改只涉及常数个指针,全部询问处理完后从沿遍历即可恢复最终排列。 建立链表和输出排列均为,处理询问为,总时间复杂度为,空间复杂度为。
void solve(){ int n, q; cin >> n >> q; vector<int> pre(n + 1), nxt(n + 1); vector<int> a(n + 1); int head = -1, tail = -1; for(int i = 1; i <= n;i++){ cin >> a[i]; if(i == 1) head = a[i]; if(i > 1){ pre[a[i]] = a[i - 1]; nxt[a[i - 1]] = a[i]; } } tail = a[n]; for(int i = 1; i <= q;i++){ int x; cin >> x; if(tail == x) continue; if(pre[x] != 0) nxt[pre[x]] = nxt[x]; else head = nxt[x]; if(nxt[x] != 0) pre[nxt[x]] = pre[x]; nxt[tail] = x; pre[x] = tail; nxt[x] = 0; tail = x; } int cur = head; while(cur){ cout << cur << " "; cur = nxt[cur]; } cout << '\n';}D - Outweigh
令,要求等价于构造正整数权值,使 。 若所有,由于每个都必须为正数,上式不可能严格大于,因此无解。 若存在,则把所有正系数对应的权值设为,其余权值设为。正系数均为整数,所以正贡献至少为;另一方面,负系数的绝对值小于,且,全部负贡献的绝对值小于。因此正贡献一定严格大于负贡献,构造合法。 只需遍历数组判断是否存在正系数并输出对应权值,时间复杂度为,空间复杂度为。
void solve(){ int n; cin >> n; vector<ll> a(n + 1), b(n + 1); vector<ll> c(n + 1); bool has_z = false; vector<int> z; for(int i = 1; i <= n;i++) cin >> a[i]; for(int i = 1; i <= n;i++) cin >> b[i]; for(int i = 1; i <= n;i++){ c[i] = a[i] - b[i]; if(c[i] > 0) {has_z = true; z.push_back(i);} } vector<ll> ans(n + 1); if(!has_z) {cout << "No" << '\n'; return;} cout << "Yes" << '\n'; for(auto &it : z){ ans[it] = (ll)1e18; } for(int i = 1; i <= n;i++){ if(ans[i] == 0) ans[i] = 1; cout << ans[i] << " "; }}E - One Time Coupon
设最终有种商品使用优惠券完成至少一次购买,其余种商品按购买。先假设所有商品都按原价购买,基础费用为。把商品改为使用优惠券后,费用变化量为。因此当固定时,应选择变化量最小的种商品;将所有排序后取前缀和即可。 按原价购买一次会产生一张优惠券,而使用优惠券购买会消耗一张。上述方案产生张、消耗张优惠券。当时,不需要额外购买;当时,还缺少张优惠券。每张缺少的优惠券都可以通过额外按原价购买任意商品获得,而重复购买的最低单价是,所以补充费用为 。 枚举,维护排序后变化量的前缀和,并对 取最小值。排序占用时间,枚举占用时间,空间复杂度为。
void solve(){ int n; cin >> n; ll base = 0; ll mia = LLONG_MAX; vector<ll> diff; for(int i = 1; i <= n;i++){ ll a, b; cin >> a >> b; base += a; mia = min(mia, a); diff.push_back(b - a); } sort(diff.begin(), diff.end()); ll ans = base, cur = base; for(int i = 1; i <= n;i++){ cur += diff[i - 1]; ll extra = max(0, 2 * i - n); ans = min(ans, cur + extra * mia); } cout << ans << '\n';}模板:
#include <bits/stdc++.h>
using namespace std;using ll = long long;using ull = unsigned long long;#define mod 998244353typedef pair<int, int> pii;typedef pair<ll, ll> pll;
ll fast_pow(ll a, ll b, ll MOD){ ll ans = 1; while(b){ if(b & 1) ans = (ans * a) % MOD; b >>= 1; a = (a * a) % MOD; } return ans;}
vector<ll> get_inverse(ll n, ll MOD){ vector<ll> inverse(n + 1, 0); inverse[1] = 1; for(int i = 2; i <= n;i++){ inverse[i] = MOD - (MOD / i) * inverse[MOD % i] % MOD; } return inverse;}
struct DSU{ vector<int> fa, sz; DSU(int n){ fa.resize(n + 1); sz.resize(n + 1, 1); for(int i = 1; i <= n;i++){ fa[i] = i; } } int find(int x){ if(fa[x] == x) return x; return fa[x] = find(fa[x]); }
bool merge(int x, int y){ x = find(x); y = find(y); if(x == y) return false; if(sz[x] > sz[y]) swap(x, y); fa[y] = x; sz[x] += sz[y]; return true; }};
void solve(){}
int main(){ ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int _ = 1; //cin >> _; while(_--){ solve(); } return 0;}