自我感觉题目难度:
简单:D B F
中等:H G E
进阶:A C I
博客园自带目录()
A
博弈论。
显而易见,对于足够聪明的人,在所有决策中只要存在一个必胜决策,就会直接选择这个策略。
故而可以使用 \(dfs\) 爆搜。
对于一个状态,一共可以从 \(5\) 个状态转移过来,可以直接存到一个数组。
int a[5] = {1, 2, 3, 5, 8};
bool dp[N];
bool dfs(int k)
{if (k == 0) // 无法取数,此状态为必输。return 0;bool ret = 0; // 判断是否存在必胜策略for (int i = 0; i < 5; i++){if (k < a[i])break;ret |= !dfs(k - a[i]);// 只需要存在一个必胜状态(对手必输)即可}return ret;
}
可以发现,此时可能会对同一个状态进行多次搜索,所以可以记忆化(类似斐波那契数列):
int a[5] = {1, 2, 3, 5, 8};
bool dp[N];
bool dfs(int k)
{if (dp[k])return dp[k];if (k == 0)return 0;bool ret = 0;for (int i = 0; i < 5; i++){if (k < a[i])break;ret |= !dfs(k - a[i]);}return dp[k] = ret;
}
B
签到。
当棉花小熊进行偶数次操作时,灯的状态不会发生改变,而奇数次则会发生改变。
\(m\) 可以直接用 \(string\) 来储存。
int n;string m;cin >> n >> m;if (m.size() & 1)n ^= 1;if (n)cout << "qwq\n";elsecout << "awa\n";
C
官方说是前缀和。
哥们不懂思维,但是哥们会数据结构。
题目的前半部分相当于是单点查询,区间求值,其中模数为 \(k\)。
可以用线段树或者树状数组解决。
不会线段树的可以移步。
而后续操作即为统计每个数出现的次数,用一个桶即可处理。
这个题好像卡时间严重,不能使用 \(map\)。
using i64 = long long;
const int N = 1e6 + 7;
int n, m, mod;
int a[N];
struct Seg_Tree
{i64 sum;int le, ri, siz;int tag;
} T[N << 2];
void pushup(int i)
{T[i].sum = (T[i << 1].sum + T[i << 1 | 1].sum) % mod;
}
void build(int i, int l, int r)
{T[i].le = l, T[i].ri = r;T[i].siz = r - l + 1;if (l == r){T[i].sum = a[l];return;}int mid = (l + r) >> 1;build(i << 1, l, mid);build(i << 1 | 1, mid + 1, r);pushup(i);
}
void pushdown(int i)
{T[i << 1].sum = (T[i << 1].sum + (i64)T[i].tag * T[i << 1].siz % mod) % mod;T[i << 1 | 1].sum = (T[i << 1 | 1].sum + (i64)T[i].tag * T[i << 1 | 1].siz % mod) % mod;T[i << 1].tag = (T[i << 1].tag + T[i].tag) % mod;T[i << 1 | 1].tag = (T[i << 1 | 1].tag + T[i].tag) % mod;T[i].tag = 0;
}
void update(int i, int l, int r, int k)
{if (r < l)return;if (l <= T[i].le && T[i].ri <= r){T[i].sum = (T[i].sum + (i64)k * T[i].siz % mod) % mod;T[i].tag = (T[i].tag + k) % mod;return;}if (T[i].tag)pushdown(i);int mid = (T[i].le + T[i].ri) >> 1;if (l <= mid)update(i << 1, l, r, k);if (mid < r)update(i << 1 | 1, l, r, k);pushup(i);
}
int ask(int i, int pos)
{if (T[i].le == T[i].ri)return T[i].sum;if (T[i].tag)pushdown(i);int mid = (T[i].le + T[i].ri) >> 1;if (pos <= mid)return ask(i << 1, pos);elsereturn ask(i << 1 | 1, pos);
}
// map<int, int> tong;
int tong[100000001];
void _()
{n = re(), m = re(), mod = re();for (int i = 1; i <= n; i++)a[i] = re();build(1, 1, n);for (int i = 1; i <= n; i++){a[i] = ask(1, i) % mod;update(1, i + 1, min(i + m, n), a[i]);}int maxn = 0, maxp = -1;for (int i = 1; i <= n; i++){// wr(a[i], ' ');int now = ++tong[a[i]];if (now > maxn){maxn = now;maxp = a[i];}else if (now == maxn && maxp > a[i]){maxp = a[i];}}wr(maxp, ' '), wr(maxn, '\n');
}
int main()
{int qwq = 1;// qwq = re();for (int i = 1; i <= qwq; i++)_();return 0;
}
D
签到。
只需要判断是不是 素数 和是不是 三的倍数 就可以了。
bool is_p(int k)
{if (k < 2)return 0;for (int i = 2; i * i <= k; i++)if (k % i == 0)return 0;return 1;
}
void _()
{int n = re();bool pd1 = is_p(n);bool pd2 = (n % 3 == 0);if (pd1 && pd2)puts("xiaofeishu");else if (pd1)puts("xiaoyang");else if (pd2)puts("xiaomaomi");elseputs("xiaoniaogongzhu");
}
E
挺好的一个思维题。
整体思路是前缀和+二分查找。
首先可以通过前缀和统计在打完第 \(i\) 个攻击之后能造成的总伤害是多少,即:
sum[0] = x;for (int i = 1; i <= n; i++)sum[i] = sum[i - 1] + a[i] + y;
然后对于每个魔物,通过 \(lower\_bound\) 找到第一个 大于等于 \(t\) 的位置即可,若找不到,输出 Failer
。
int x = re(), y = re();int n = re();for (int i = 1; i <= n; i++)a[i] = re();sum[0] = x;for (int i = 1; i <= n; i++)sum[i] = sum[i - 1] + a[i] + y;int s = re();while (s--){int t = re();int it = lower_bound(sum, sum + n + 1, t) - sum;if (it == n + 1)puts("Failer");elsewr(it, '\n');}
F
签到。
判断 \(i\) 的就行来判断加减即可。
int n = re(), ans = 0;for (int i = 1; i <= n; i++){int ls = re();if (i & 1)ans += ls;elseans -= ls;}wr(ans, '\n');
G
最大子段和板子,只是非空。
在输入的过程中直接求和 \(sum\),并同时与 \(ans\) 做比较取 \(max\),如果遇到 \(sum<0\) 的情况则说明此时求的 \(sum\) 对于之后的贡献是负的,直接将 \(sum\) 清零即可。
int n = re(), ans = -1e9;int sum = 0;for (int i = 1; i <= n; i++){int ls = re();sum += ls;ans = max(ans, sum);if (sum < 0)sum = 0;}wr(ans, '\n');
H
经典的边问边找。
用一个 \(map\) 储存值所对应的下标,如果对于新输入的 \(k\),存在 \(target-k\),则说明存在情侣,直接输出便可 \(return\)。
本题没有多组测试,若存在多组测试,则需要保证每组数据全部输入完。
int n = re(), m = re();for (int i = 1; i <= n; i++){int k = re();if (T[m - k]){wr(T[m - k] - 1, ' '), wr(i - 1, '\n');return;}T[k] = i;}
I
计算几何,数学题,需要分类讨论。
- 相离
如图。
此时相交的面积为 0。
- 包含
如图。
此时相交的面积为较小的圆的面积。
- 相交
如图。
此时的面积为两段弧之间所包含的面积。
已知 \(O_1,O_2\) 的坐标,可以求出距离 \(d\)。
可以通过中间的绿线分为左右两个弓形部分,能够分别求出。
以红色方 \(O_1\) 为例,可以通过 \(r_1,r_2\) 和 \(d\) 求出 \(A\)(余弦公式 \(a^{2}=b^{2}+c^{2}-2bc\cos A\) 和反三角函数 \(\arccos\))。
此时圆心角即为 \(\alpha=2A\)。
然后红色弓形的面积就是 \(S_{扇形}-S_{\Delta}\),可以通过扇形面积公式和三角形面积公式即可求出。
struct yuan
{double x, y, r;
} r1, r2;
double p2(double x) { return x * x; }
double dis(yuan a, yuan b)
{return sqrt(p2(a.x - b.x) + p2(a.y - b.y));
}
void _()
{cout << fixed << setprecision(1);cin >> r1.x >> r1.y >> r1.r;cin >> r2.x >> r2.y >> r2.r;double d = dis(r1, r2);if (d >= (r1.r + r2.r)){ // 相离cout << 0.0 << '\n';return;}if (d + min(r1.r, r2.r) <= max(r1.r, r2.r)){ // 包含cout << acos(-1) * p2(min(r1.r, r2.r)) << '\n';return;}double alpha1 = 2.0L * acos((p2(r1.r) + p2(d) - p2(r2.r)) / (2.0L * r1.r * d));double san1 = p2(r1.r) * sin(alpha1) / 2.0L;double shan1 = p2(r1.r) * alpha1 / 2.0L;double sub1 = shan1 - san1;double alpha2 = 2.0L * acos((p2(r2.r) + p2(d) - p2(r1.r)) / (2.0L * r2.r * d));double san2 = p2(r2.r) * sin(alpha2) / 2.0L;double shan2 = p2(r2.r) * alpha2 / 2.0L;double sub2 = shan2 - san2;cout << sub1 + sub2;
}
signed main()
{int qwq = 1;// qwq = re();for (int i = 1; i <= qwq; i++)_();return 0;
}