感觉对于诸神来说这么一套水题题解没啥意义啊,
作者:hans774882968以及hans774882968
A
估计最大值的下界n/3+1
,直接枚举最大值就能过:
read (n);
int v1, v2, v3;
rep (i, n / 3 + 1, n) {
if ( (n - i) % 2) v1 = (n - i) / 2, v2 = v1 + 1;
else v1 = (n - i) / 2 - 1, v2 = v1 + 2;
if (v1 < v2 && v2 < i) {
v3 = i;
break;
}
}
printf ("%d %d %d\n", v2, v3, v1);
不过输入n = 6 7 8 9 ...
可以发现最大值是可以直接写出来的式子(n - 1) / 3 + 2
,所以可以进一步优化:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
int T;
read (T);
while (T--) {
read (n);
int v1, v2, v3 = (n - 1) / 3 + 2;
if ( (n - v3) % 2) v1 = (n - v3) / 2, v2 = v1 + 1;
else v1 = (n - v3) / 2 - 1, v2 = v1 + 2;
printf ("%d %d %d\n", v2, v3, v1);
}
return 0;
}
B
分b[i]
等于和不等于0讨论。
- 对于
b[i]≠0
的i
,要求a[i]-b[i]
相同,设为x
。 - 对于其他
i
,要求max(a[i]) <= x
。
用set实现最方便。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, a[N], b[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
bool jdg() {
set<int> s1, s2;
rep (i, 1, n) {
if (b[i]) s1.insert (a[i] - b[i]);
else s2.insert (a[i]);
}
if (s1.size() > 1) return false;
if (s1.size() == 1 && *s1.begin() < 0) return false;
if (s1.size() == 1 && s2.size() && *s1.begin() < *s2.rbegin() ) return false;
return true;
}
int main() {
int T;
read (T);
while (T--) {
read (n);
rep (i, 1, n) read (a[i]);
rep (i, 1, n) read (b[i]);
puts (jdg() ? "YES" : "NO");
}
return 0;
}
C
只需要求实际的开始时间,其表达式看样例就很容易写出来。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, a[N], b[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
int T;
read (T);
while (T--) {
read (n);
rep (i, 1, n) read (a[i]);
rep (i, 1, n) read (b[i]);
rep (i, 1, n) printf ("%d%c", b[i] - max (b[i - 1], a[i]), " \n"[i == n]);
}
return 0;
}
D
直接枚举一个长为k
的区间,看里面有多少个W
即可。用前缀和统计W
个数。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, a[N], k;
char s[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
int T;
read (T);
while (T--) {
read (n, k);
scanf ("%s", s + 1);
rep (i, 1, n) a[i] = a[i - 1] + (s[i] == 'W');
int ans = n;
rep (i, k, n) ans = min (ans, a[i] - a[i - k]);
printf ("%d\n", ans);
}
return 0;
}
E
对于有定值掺杂其中的式子,总是建议先尝试把定值提取出来,再进行进一步分析。——沃·兹基硕德
(a + b) / k = a / k + b / k + (a % k + b % k) / k
,这就把定值a / k
和b / k
提取出来了,后续只需要考虑a % k + b % k >= k
是否成立。
这是一个很经典的贪心问题,好像是以过河为背景来着?贪心策略:先排序,再试图为当前的最小值a[i]
匹配一个最小的满足条件的值。若匹配失败则丢弃a[i]
,成功则获得一对。用multiset来进行匹配操作即可。
有另一种做法是排序+双指针,即直接拿现有的最大值去匹配当前的最小值。这种做法也能AC,但我不清楚其正确性。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, k, a[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
int T;
read (T);
while (T--) {
read (n, k);
rep (i, 1, n) read (a[i]);
LL ans = 0;
rep (i, 1, n) ans += a[i] / k, a[i] %= k;
sort (a + 1, a + n + 1);
multiset<int> st;
rep (i, 1, n) st.insert (a[i]);
rep (i, 1, n) {
if (!st.size() ) break;
if (!st.count (a[i]) ) continue;
st.erase (st.find (a[i]) );
auto it = st.lower_bound (k - a[i]);
if (it == st.end() ) continue;
st.erase (it);
++ans;
}
printf ("%lld\n", ans);
}
return 0;
}
F
前置芝士:一个置换可以写成若干个循环的乘积。如输入数据的8 6 1 7 5 2 9 3 10 4
写成(1 8 3) (2 6) (4 7 9 10) (5)
。一个置换的阶等于其所有循环的阶的lcm,符合直觉很好理解。
对于循环(a[1] a[2] ... a[x])
,取出字符串t = s[a[1]]s[a[2]]...s[a[x]]
,那么变换一次就表示把首字符移到末尾。所以我们构造一个t + t
,t
变换回原串的次数就等于(t + t).find(t, 1)
,其中find
是cpp的string的函数,第二个参数表示从哪个下标开始匹配子串。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e2 + 5;
int n, p[N];
bool vis[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
LL gcd (LL a, LL b) {
return b ? gcd (b, a % b) : a;
}
LL lcm (LL a, LL b) {
return a / gcd (a, b) * b;
}
int main() {
int T;
read (T);
while (T--) {
memset (vis, 0, sizeof vis);
read (n);
string s;
cin >> s;
re_ (i, 0, n) read (p[i]), p[i]--;
LL ans = 1;
re_ (i, 0, n) {
if (vis[i]) continue;
vis[i] = true;
int u = p[i];
string t = string (1, s[i]);
while (u != i) {
vis[u] = true;
t += s[u];
u = p[u];
}
int cnt = (t + t).find (t, 1);
ans = lcm (ans, cnt);
}
printf ("%lld\n", ans);
}
return 0;
}
G
以样例10 13 5 2 6
为例。首先求出所有形成的区间[1, 2] [3, 3] [4, 5]
。若a[2] -= 3
,因为大于等于该区间的值,所以没有发生区间的分裂。若a[2] -= 8
,因为小于该区间的值,所以需要进行区间分裂,即idx = 2
成为新的区间左端点,之后要找出新的区间的右端点,即进行区间合并,[3, 3]
被合并进去了。如果a[2]
再小一些,变成a[2] <= 2
,则[3, 3] [4, 5]
都会被合并进去。
从上面的例子发现,需要进行区间的分裂和合并。合并时要一直遍历到候选区间的值严格小于新产生的区间的值为止。因为区间之间没有缝隙,所以我们只需要用一个值来表示,比如上面的例子就是1 3 4
。此时可以梳理我们需要的操作:
- 区间分裂,即插入一个值。
- 区间合并,即删除若干个值。
- 寻找最大的小于等于
k
的值。
这些操作只需要set。
时间复杂度:O((n + m)logn)
。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, m, a[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
int T;
read (T);
while (T--) {
read (n, m);
rep (i, 1, n) read (a[i]);
set<int> s;
for (int mn = INT_MAX, i = 1; i <= n; ++i) {
if (mn > a[i]) s.insert (i);
mn = min (mn, a[i]);
}
while (m--) {
int k, d;
read (k, d);
a[k] -= d;
auto it = --s.upper_bound (k);
auto it_tmp = it;
for (++it; it != s.end() && a[*it] >= a[k]; it = s.erase (it) );
if (a[*it_tmp] > a[k]) s.insert (k);
// for (int v : s) cout << v << ",";
// puts (""); //
printf ("%d%c", s.size(), " \n"[m == 0]);
}
}
return 0;
}