自我感觉题目难度:
简单: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";
else
cout << "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);
else
return 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");
else
puts("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");
else
wr(it, '\n');
}F
签到。
根据 \(i\)
int n = re(), ans = 0;
for (int i = 1; i <= n; i++)
{
int ls = re();
if (i & 1)
ans += ls;
else
ans -= 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
计算几何,数学题,需要分类讨论。
1. 相离
如图。

此时相交的面积为 0。
2. 包含
如图。

此时相交的面积为较小的圆的面积。
3. 相交
如图。

此时的面积为两段弧之间所包含的面积。
已知 \(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}\),可以通过扇形面积公式和三角形面积公式即可求出。
\[S_{扇形}=\dfrac{1}{2} r^2 \alpha\\ S_{\Delta}=\dfrac{1}{2} r^2\sin{\alpha}\]
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;
}
















