2022 CCPC 女生赛
A. 减肥计划
因为当\(k\ge n\)时,最大值一定是答案。并且队头的值是不会减少的。所以从对头出队又从队尾入队的一定不是答案。所以最多进行\(n\)次,用双端队列模拟一下即可。
#include <bits/stdc++.h>
using namespace std;
int32_t main() {
int n , k , maxVal = 0, res = 0;
scanf("%d%d" , &n,&k);
deque<pair<int, int> > q;
for (int i = 1, x; i <= n; i++)
scanf("%d" , &x ) , maxVal = max(maxVal, x), q.emplace_back(x, i);
int cnt = 0, cntID = 0;
for (int i = 1 , aVal , aID , bVal , bID; i <= n; i++) {
aVal = q.front().first , aID = q.front().second , q.pop_front();
bVal = q.front().first , bID = q.front().second , q.pop_front();
if (aVal == maxVal) {
res = aID;
break;
}
if (aVal >= bVal) {
if (cntID == aID) cnt++;
else cntID = aID, cnt = 1;
if (cnt >= k) {
res = aID;
break;
}
q.emplace_front(aVal, aID), q.emplace_back(bVal, bID);
} else {
cntID = bID, cnt = 1;
if (cnt >= k) {
res = bID;
break;
}
q.emplace_back( aVal , aID ) , q.emplace_front( bVal , bID );
}
}
printf("%d\n", res);
return 0;
}
C. 测量学
因为两个方向都可以走所以首先要\(\theta = \min(\theta,2\pi-\theta)\),大圆走小圆绕的距离是\(2(R-r)+\theta r\),也可以直接走大圆就是\(\theta R\),枚举取最小值即可
#include <bits/stdc++.h>
using namespace std;
int32_t main() {
long double n , r , p , res , pi=3.14159265358979323846;
cin >> n >> r >> p;
p = min( p , 2.0 * pi - p );
res = r * p;
for( int i = 1 ; i < n ; i ++ ){
long double x; cin >> x;
res = min( res , 2 * ( r - x ) + x * p );
}
cout << fixed << setprecision(6) << res;
return 0;
}
E. 睡觉
因为可以循环做,所以我采用了一个技巧破环成链,直接把a
数组存两边。
然后如果两边可以直接睡着的,就可以直接结束。然后我们考虑需要重复很多遍的情况。
如果完整的播完一遍之后,清醒度比一开始要低的,则一定可以睡着。如果播完一遍后,清醒度比一开始要高,则一定无法入睡。如果保持不变,此时则要判断过程中清醒度时候一直小于等k
如果小于,足够的时间后也可以入睡
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void solve() {
int x = read(), t = read(), k = read(), n = read(), d = read();
vector<int> a(2 * n + 5), f(2 * n + 5);
for (int i = 1; i <= n; i++) a[i] = a[i + n] = read();
f[0] = x;
for (int i = 1 , cnt = 0 ; i <= n * 2; i++) {
if (a[i] <= d) f[i] = max(f[i - 1] - 1, 0ll);
else f[i] = f[i - 1] + 1;
if( f[i] <= k ) cnt ++;
else cnt = 0;
if( cnt >= t ){
printf("YES\n");
return;
}
}
if( f[n] < f[0] ){
printf("YES\n");
return;
}else if( f[n] == f[0] && f[0] == k && t >= n ){
bool flag = true;
for( int i = 1 ; flag && i <= n ; i ++ )
if( f[i] > k ) flag = false;
if(flag) printf("YES\n");
else printf("NO\n");
}else printf("NO\n");
return;
}
int32_t main() {
for (int t = read(); t; t--) solve();
return 0;
}
G. 排队打卡
这道题的做法就是模拟这个过程就好,但是要注意一个细节排队是在这一秒开始的时候进行,但进入入口是在这一秒结束的时候进行。
在模拟的过程中验证n
时候正确并且枚举排队的时间,计算一下进入的时间就好
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int32_t main() {
int t = read(), n = read(), m = read(), k = read(), res, val = LLONG_MAX;
vector<pair<int, int>> p(m);
for (auto &[ti, xi]: p) ti = read(), xi = read();
p.emplace_back(t, 0);
sort(p.begin(), p.end());
int last = 0, preTime = p.front().first - 1;
for (auto [ti, xi]: p) {
// ti 秒初
last = max(0ll, last - k * (ti - preTime - 1));
if (ti == t) {
if (last != n) cout << "Wrong Record\n", exit(0);
continue;
}
last += xi;
if (ti > t) {
int ans = (last + k) / k;
if (ans <= val) val = ans, res = ti;
}
// ti 秒末
preTime = ti, last = max(0ll, last - k);
}
cout << res << " " << val << '\n';
return 0;
}
H. 提瓦特之旅
如果没有委托这个条件的话,这就是一个最短路的题目了。我们还要解决的问题是委托,委托其实和访问哪些点无关,之和访问点的个数有关。并且这里点数只有500。我们可以在计算最短路的过程中记录f[i][j]
表示到达点i
经过j
个点的最短路。这样直接用bfs
计算f[i][j]
,然后\(O(n)\)的回答就好,复杂度\(O(nq)\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
typedef pair<int, int> PII;
const int inf = 1e17;
int32_t main() {
int n = read(), m = read();
vector<vector<PII>> e(n + 1);
vector<vector<int>> f(n + 1, vector<int>(n + 1, inf ) );
for (int u, v, w; m; m--)
u = read(), v = read(), w = read(), e[u].emplace_back(v, w), e[v].emplace_back(u, w);
f[1][0] = 0;
queue<pair<int, int> > q;
q.emplace(1, 0);
while (!q.empty()) {
auto [u, step] = q.front();
q.pop();
if( step == n-1 ) continue;
for (auto [v, w]: e[u]) {
if (f[v][step + 1] <= f[u][step] + w) continue;
f[v][step + 1] = f[u][step] + w;
q.emplace( v , step + 1 );
}
}
for( int i = 1 ; i <= n ; i ++ )
for( int j = 1 ; j < n ; j ++ )
f[i][j] = min( f[i][j] , f[i][j-1] );
for( int q = read() , t , res ; q ; q -- ){
vector<int> w(n);
for( auto & i : w ) i = read();
t = w[0] , res = LLONG_MAX ;
for( int i = 1 , sum = 0 ; i < n ; i ++ ){
sum += w[i];
if( f[t][i] == inf ) continue;
res = min( res , f[t][i] + sum );
}
printf("%lld\n" , res );
}
return 0;
}
I. 宠物对战
f[i][0/1]
表示前i
位,且最后一位是a/b是否成立
然后我们枚举当前这一个字符串的长度,然后去查询这个字符串是否再对应的集合中存在即可。查询,我使用了Trie树实现。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int sonB[N][26], cntB[N], idxB;
int sonA[N][26], cntA[N], idxA;
void insert(string s, int son[][26], int cnt[], int &idx) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int u = s[i] - 'a';
if (!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
int query(string s, int son[][26], int cnt[], int &idx) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int u = s[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int f[N][2];
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
string s;
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> s, insert(s, sonA, cntA, idxA);
cin >> n;
for (int i = 1; i <= n; i++) cin >> s, insert(s, sonB, cntB, idxB);
cin >> s;
n = s.size() , s = " "+s;
vector<array<int, 2> > f(n + 1);
for (int i = 1; i <= n; i++) f[i][0] = f[i][1] = INT_MAX;
for (int i = 0; i <= n; i++) {
if (f[i][1] < INT_MAX) {
int p = 0;
for (int j = i + 1 , u ; j <= n ; j ++ ){
u = s[j] - 'a';
if( sonA[p][u] == 0 ) break;
p = sonA[p][u];
if( cntA[p] ) f[j][0] = min( f[j][0] , f[i][1] +1 );
}
}
if( f[i][0] < INT_MAX ){
int p = 0;
for( int j = i + 1 , u ; j <= n ; j ++ ){
u = s[j] - 'a';
if( sonB[p][u] == 0 ) break;
p = sonB[p][u];
if( cntB[p] ) f[j][1] = min( f[j][1] , f[i][0] + 1 );
}
}
}
int res = min(f[n][1], f[n][0]);
if (res == INT_MAX) res = -1;
cout << res;
}