AGC001 题解
A - BBQ Easy
先将
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 205;
int n, a[N];
int main() {
int i;
n = Read(), n <<= 1;
for(i = 1; i <= n; i++) {
a[i] = Read();
}
sort(a + 1, a + n + 1);
int ans = 0;
for(i = 1; i <= n; i += 2) {
ans += a[i];
}
Write(ans), putchar('\n');
return 0;
}
B - Mysterious Light
可以看到,从光反射第二次后开始后,以两轮为周期,光可能经过的范围总是为一个平行四边形,且光总是沿着平行四边形某个
比如样例的图:
设两条边长为
显然初始两条光线总长为
观察这个式子,首先可以令边界为
观察到当
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
ll Solve(ll x, ll y) {
return y ? Solve(y, x % y) + 2ll * (x / y) * y : -x;
}
int main() {
ll n = Read(), x = Read();
Write(n + Solve(n - x, x));
return 0;
}
C - Shorten Diameter
没看数据范围,结果一看
考虑暴力枚举直径的中心,对
- 当
为奇数时,枚举一条边,将与边的两个端点的距离最小值小于等于 的点删去。 - 当
为偶数时,枚举一个点,将与点的距离小于等于 的点删去。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 2005;
int n, cnt = 0, k, o;
vector<int> e[N];
bool vis[N];
void Dfs(int u, int dis) {
vis[u] = true;
if(dis > k / 2) {
cnt++;
}
for(auto v : e[u]) {
if(vis[v] || v == o) {
continue;
}
Dfs(v, dis + 1);
}
}
int main() {
int i;
n = Read(), k = Read();
vector<pair<int, int> > vec;
for(i = 1; i < n; i++) {
int u = Read(), v = Read();
vec.emplace_back(u, v);
e[u].push_back(v), e[v].push_back(u);
}
int ans = INT_MAX;
if(k & 1) {
for(auto p : vec) {
int u = p.first, v = p.second;
memset(vis, 0, sizeof(vis)), cnt = 0;
o = v, Dfs(u, 0), o = u, Dfs(v, 0);
ans = min(ans, cnt);
}
}
else {
for(i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis)), cnt = 0;
Dfs(i, 0);
ans = min(ans, cnt);
}
}
Write(ans);
return 0;
}
D - Arrays and Palindrome
注意到:
- 对于重叠的两个回文限制,长度分别为
,且两个回文限制的初始位置(或结束位置,同理)相同,则可以推出这个长度为 的序列相同。 - 对于重叠的两个回文限制,长度均为
且 是偶数,且两个回文限制的初始位置(或结束位置,同理)相差 ,则可以推出这个长度为 的序列相同。 - 对于一个长度为
的回文限制,最多会产生 个两个数相等的限制条件,而判断 个数相等至少需要 个限制条件,所以若 中有三个及以上的奇数一定无解。 - 否则,把奇数尽量排在边缘是最优的。
可以构造:
特判
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 100005;
int n, m, a[N];
int main() {
int i, cnto = 0;
n = Read(), m = Read();
for(i = 1; i <= m; i++) {
a[i] = Read();
if(a[i] & 1) {
cnto++;
}
}
if(m == 1) {
if(a[1] == 1) {
printf("1\n1\n1");
}
else {
printf("%d\n2\n%d %d", a[1], a[1] - 1, 1);
}
return 0;
}
if(cnto > 2) {
printf("Impossible");
return 0;
}
for(i = 1; i <= m; i++) {
if(a[i] & 1) {
swap(a[1], a[i]);
break;
}
}
for(i = m; i; i--) {
if(a[i] & 1) {
swap(a[m], a[i]);
break;
}
}
vector<int> b;
if(a[1] > 1) {
b.push_back(a[1] - 1);
}
for(i = 2; i < m; i++) {
b.push_back(a[i]);
}
b.push_back(a[m] + 1);
for(i = 1; i <= m; i++) {
Write(a[i]), putchar(' ');
}
int k = b.size();
putchar('\n'), Write(k), putchar('\n');
for(i = 0; i < k; i++) {
Write(b[i]), putchar(' ');
}
return 0;
}
E - BBQ Hard
直接做是不好做的。
注意到组合数,因此考虑这个东西的组合意义。
首先有:
对于
将点向左平移
因此考虑 DP,初始给每个
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 200005, M = 4005;
const ll Mod = 1e9 + 7;
ll f[M][M], fact[N], invfact[N], inv[N];
void Init(int n) {
int i;
inv[1] = 1;
for(i = 2; i <= n; i++) {
inv[i] = Mod - Mod / i * inv[Mod % i] % Mod;
}
fact[0] = invfact[0] = 1;
for(i = 1; i <= n; i++) {
fact[i] = fact[i - 1] * i % Mod;
invfact[i] = invfact[i - 1] * inv[i] % Mod;
}
}
ll C(ll n, ll m) {
if(n < m || n < 0) {
return 0;
}
return fact[n] * invfact[m] % Mod * invfact[n - m] % Mod;
}
int n, a[N], b[N];
ll QuickPow(ll x, ll y) {
if(y == 0) {
return 1;
}
if(y == 1) {
return x;
}
ll half = QuickPow(x, y >> 1);
if(y & 1) {
return half * half % Mod * x % Mod;
}
return half * half % Mod;
}
int main() {
Init(N - 5);
int i, j;
n = Read();
for(i = 1; i <= n; i++) {
a[i] = Read(), b[i] = Read();
f[2001 - a[i]][2001 - b[i]]++;
}
for(i = 1; i <= 4002; i++) {
for(j = 1; j <= 4002; j++) {
f[i][j] = (f[i][j] + f[i - 1][j] + f[i][j - 1]) % Mod;
}
}
ll ans = 0;
for(i = 1; i <= n; i++) {
ans = (ans + f[2001 + a[i]][2001 + b[i]]) % Mod;
ans = (ans - C(2 * a[i] + 2 * b[i], a[i] * 2) + Mod) % Mod;
}
Write(ans * QuickPow(2, Mod - 2) % Mod);
return 0;
}
F - Wide Swap
参考 @linghuchong_ 的神仙题解。
对于排列
显然在排列
问题转化为,每次可以交换
对于字典序最小的排列
考虑如何对
做到
对于两个已经按上述规则“排序”的序列,假设为序列
现在如果当前
否则,假设当前
, 。
反之,
, 。
这一条件可以拆成两个条件(两个条件成立一个即可):
; 。
其中,第二种情况若满足,则
等一下,这个条件貌似漏了一些情况,若满足上述条件,
即,
但实际上我们可以证明,不存在不满足上述情况的且已被“排序”的
假设存在满足上述情况且已被“排序”的
,那么 可以被一定分成两个集合 ,使得 ,且 。
这样的话,, 。
因此的数不能被 中的数阻挡,而 的数一定小于 的数,所以 的数应该被交换到最前面,所以 并未被完全排序,矛盾。
因此,只要
否则,我们只能把
代码实现时,可以预处理
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 500005;
int n, K, p[N], q[N], t[N], minn[N];
void Merge(int l, int mid, int r) {
minn[mid + 1] = N;
int i, j, k;
for(i = mid; i >= l; i--) {
minn[i] = min(minn[i + 1], q[i]);
}
i = l, j = mid + 1, k = l;
while(i <= mid && j <= r) {
if(minn[i] - K >= q[j]) {
t[k++] = q[j++];
}
else {
t[k++] = q[i++];
}
}
while(i <= mid) {
t[k++] = q[i++];
}
while(j <= r) {
t[k++] = q[j++];
}
for(i = l; i <= r; i++) {
q[i] = t[i];
}
}
void Solve(int l, int r) {
if(l >= r) {
return ;
}
int mid = (l + r) >> 1;
Solve(l, mid), Solve(mid + 1, r), Merge(l, mid, r);
}
int main() {
int i;
n = Read(), K = Read();
for(i = 1; i <= n; i++) {
p[i] = Read(), q[p[i]] = i;
}
Solve(1, n);
for(i = 1; i <= n; i++) {
p[q[i]] = i;
}
for(i = 1; i <= n; i++) {
Write(p[i]), putchar('\n');
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效