2022年中国大学生程序设计竞赛女生专场 ACEGHIL
A. 减肥计划
思路:因为\(k\)很大但是\(n\)只有\(1e6\),那么分两种情况考虑:
- 对于\(k\ge n\)的情况,那么一定所有人都比完了,因为\(n\)轮一定可以确定最后的获胜者。那么答案就是最重的。
- 对于\(k<n\)的情况,直接暴力模拟,最多不超过\(1e6\)次。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e6 + 10;
array<int,2>a[N];
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int n,k;
cin>>n>>k;
deque<array<int,2>>q;
int maxx = 0,pos = 0;
for(int i = 1;i <= n; i++)
{
int w; cin>>w;
a[i] = {w,i};
q.push_back(a[i]);
if(w>maxx)
maxx = w,pos = i;
}
if(k>=n)
{
cout<<pos<<"\n";
return 0;
}
int cnt = 0;
while(1)
{
auto fi = q.front();
q.pop_front();
auto se = q.front();
q.pop_front();
bool ok = false;
while(1)
{
if(cnt>=k)
{
pos = fi[1];
ok = true;
break;
}
if(fi[0]>=se[0])
{
cnt++;
q.push_back(se);
se = q.front();
q.pop_front();
}
else{
q.push_back(fi);
q.push_front(se);
cnt = 1;
break;
}
}
if(ok)
break;
}
cout<<pos<<"\n";
return 0;
}
C. 测量学
思路:考虑钝角还是锐角,如果是钝角那么我们考虑取它的补角。枚举每一种情况取\(min\)即可。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const double pi = 3.1415926535;
long double a[N];
long double pre[N];
int main()
{
//ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int n;
long double r,sita;
cin>>n>>r>>sita;
for(int i = 1;i <= n; i++)
cin>>a[i];
if(sita>pi)
sita = pi*2-sita;
long double ans = sita*r;
for(int i = 1;i <= n; i++)
{
ans = min(ans,sita*a[i]+(r-a[i])*2);
}
printf("%.8Lf\n",ans);
return 0;
}
E. 睡觉
思路:这题卡了很久啊,没注意到如果一轮下来清醒度不变的情况。本题分三种情况讨论:
- 一首歌的时间内能睡着
- 一轮下来清醒度递减
- 一轮下来清醒度不变但是仍然满足清醒度\(\le k\)的条件
具体代码里有写
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll a[N];
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int tc;
cin>>tc;
while(tc--)
{
ll x,t,k,n,d;
cin>>x>>t>>k>>n>>d;
for(int i = 1;i <= n; i++)
cin>>a[i];
ll tx = x,now = 0;
bool ok = false;
int cnt = 0,i = 1;
while(cnt<=1e7)
{
if(a[i]<=d)tx--;
else tx++;
if(tx<=k)
now++;
else now = 0;
if(now>=t){
ok = true;
break;
}
if(i==n)//注意这里!如果没写的话就不能判断一轮以内是否可以。
{
if(tx<x)
{
ok = true;
break;
}
}
i++;
if(i>n)i = 1;
cnt++;
}
if(ok||now>n*2)//now>n*2代表时间无限。因为它持续2轮都满足清醒度<=k,那么无限时间内一定可以
cout<<"YES\n";
else
cout<<"NO\n";
}
return 0;
}
总结:这题细节很多,写的时候要注意考虑对所以情况的分析。
G. 排队打卡
思路:这题直接模拟就可以了,但是注意细节。
【在每一秒结束前会放一次排队的人进入入口,一次最多进入不超过 k个人】的意思是,每次会放k人进入,不超过k人,但是小于k人的时候放当前的所有人。
按照这样的题意的话,那么每一秒的人数是确定的了,我们只要去check每一个有人排队的时刻即可。
为了好写一点,我们加入一个时间为\(t\),排队人数是\(0\)的事件,这样方便check。因为题目输入不会有\(0\)人排队的输入(\(x_i\ge 1\))。
注意以时间为第一关键字排序,还有注意记录\(last:\)上一个有人排队的时间,因为在这时间之间也是可以放人的。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 5e5 + 10;
array<ll,2>evt[N];
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
ll t,n,m,k;
cin>>t>>n>>m>>k;
for(int i = 1;i <= m; i++)
cin>>evt[i][0]>>evt[i][1];
m++;
evt[m][0] = t,evt[m][1] = 0;
sort(evt+1,evt+1+m);
ll hav = 0;
int last = 0;
ll mn = 1e18,pos = 0;
for(int i = 1;i <= m; i++)
{
hav = max(0ll,hav - (evt[i][0]-last-1)*k);
hav += evt[i][1];
last = evt[i][0];
//cout<<"evt = "<<evt[i][0]<<" "<<hav<<"\n";
if(evt[i][1] == 0 && hav!=n)
{
cout<<"Wrong Record\n";
return 0;
}
if(evt[i][1] != 0 &&evt[i][0] >= t)
{
if(mn>=(hav+1)/k+((hav+1)%k!=0))
mn = (hav+1)/k+((hav+1)%k!=0),pos = evt[i][0];
}
hav = max(0ll,hav-k);
}
cout<<pos<<" "<<mn<<"\n";
return 0;
}
H. 提瓦特之旅
思路:看到【经过路上的第 \(i\)个点时 \(YahAHa\) 需要 \(w_i\) 的时间完成委托】可以想到 到\(t\)号点经过\(k\)条边的最小花费。
这样就是典型的\(bellman\)了,再做一个\(dp\)就可以了。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1010,M = 4e5+10;
ll n, m, k, dist[N], backup[N],cnt = 0;
struct EDGES
{
ll a, b, w;
}edge[M];
ll f[N][N],pre[N];
void bellman_ford()
{
memset(dist, 0x3f, sizeof dist);
memset(f, 0x3f, sizeof f);
dist[1] = 0;
for(int i = 1; i < n; i++)//经过i条边
{
memcpy(backup, dist, sizeof dist);
for(int j = 0; j < m*2; j++)
{
auto e = edge[j];
dist[e.b] = min(dist[e.b], backup[e.a] + e.w);
}
for(int j = 1; j <= n; j++)
{
f[i][j] = dist[j];
}
}
}
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
for(int i = 0; i < m; i++)
{
ll u,v,w; cin>>u>>v>>w;
edge[cnt++] = {u,v,w};
edge[cnt++] = {v,u,w};
}
bellman_ford();
int q;
cin>>q;
while(q--)
{
int t; cin>>t;
for(int i = 1;i <= n-1; i++)
{
ll w; cin>>w;
pre[i] = pre[i-1]+w;
}
ll res = 1e18;
for(int i = 1;i < n; i++)
{
if(f[i][t]!=0)
res = min(res,f[i][t] + pre[i]);
}
cout<<res<<"\n";
}
cout<<"\n";
return 0;
}
引用一篇文章:关于Bellman_ford算法解决有边数限制最短路(打印路径)
I. 宠物对战
思路:一眼字符串哈希+\(dp\)。
我先分别预处理出\(A\)类和\(B\)类的\(hash\)值,放到\(set\)里面。然后做\(dp\)。
我们定义:\(dp[i][0/1]\)表示,前\(i\)位,第\(i\)位放\(A\)或放\(B\)的最少需要的字符串个数。
我们枚举左右端点,转移就是:
if(dp[l-1][0]<(1e10)&&b.find(ha.get(l,r))!=b.end())
dp[r][1] = min(dp[r][1],dp[l-1][0]+1);
if(dp[l-1][1]<(1e10)&&a.find(ha.get(l,r))!=a.end())
dp[r][0] = min(dp[r][0],dp[l-1][1]+1);
dp思路很容易,但是注意用双哈希防止碰撞,还有就是这题卡常,注意一下。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 5e5 + 10;
typedef pair<long long, long long> pll;
struct DoubleStringHash
{
vector<long long> h1, h2, w1, w2;
long long base1 = 131, base2 = 13331;
long long p1 = 1e9 + 7, p2 = 1e9 + 9;
void init(string s) {
int len = s.size();
s = " " + s;
h1.resize(len + 1), w1.resize(len + 1);
h2.resize(len + 1), w2.resize(len + 1);
h1[0] = 0, w1[0] = 1;
h2[0] = 0, w2[0] = 1;
for(int i = 1; i <= len; i++) {
h1[i] = (h1[i - 1] * base1 + s[i]) % p1, w1[i] = w1[i - 1] * base1 % p1;
h2[i] = (h2[i - 1] * base2 + s[i]) % p2, w2[i] = w2[i - 1] * base2 % p2;
}
}
pll get(int l, int r) {
return {(h1[r] - h1[l - 1] * w1[r - l + 1] % p1 + p1) % p1, (h2[r] - h2[l - 1] * w2[r - l + 1] % p2 + p2) % p2};
}
}ha;
ll dp[N][3],lena[N],lenb[N];
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int n; cin>>n;
set<pll>a,b;
for(int i = 1;i <= n; i++)
{
string x; cin>>x;
ha.init(x);
int sz = x.size();
a.insert(ha.get(1,sz));
}
int m; cin>>m;
for(int i = 1;i <= m; i++)
{
string x; cin>>x;
ha.init(x);
int sz = x.size();
b.insert(ha.get(1,sz));
}
string s; cin>>s;
ha.init(s); int sz = s.size();
memset(dp,0x3f,sizeof(dp));
dp[0][1] = dp[0][0] = 0;
for(int l = 1; l <= sz; l++)
{
for(int r = l; r <= sz; r++)
{
if(dp[l-1][0]<(1e10)&&b.find(ha.get(l,r))!=b.end())
dp[r][1] = min(dp[r][1],dp[l-1][0]+1);
if(dp[l-1][1]<(1e10)&&a.find(ha.get(l,r))!=a.end())
dp[r][0] = min(dp[r][0],dp[l-1][1]+1);
}
}
ll ans = min(dp[sz][0],dp[sz][1]);
if(ans>=1e10)
cout<<-1<<"\n";
else cout<<ans<<"\n";
return 0;
}
L. 彩色的树
思路:DSU on Tree 的模板题。套板子即可。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, k, q;
vector<int> e[N];
int l[N], r[N], id[N], sz[N], hs[N], tot;
int res[N],col[N],cnt[N],dep[N],now;
vector<int>del_c[N*2];
inline void add(int u)
{
if(cnt[col[u]]==0)now++;
cnt[col[u]]++;
del_c[dep[u]].push_back(col[u]);
}
inline void del(int depth)
{
for(auto x : del_c[depth])
{
cnt[x]--;
if(cnt[x]==0)now--;
}
del_c[depth].clear();
}
inline void query(int u)
{
res[u] = now;
}
void dfs_init(int u,int f) {
dep[u] = dep[f] + 1;
l[u] = ++tot;
id[tot] = u;
sz[u] = 1;
hs[u] = -1;
for (auto v : e[u]) {
if (v == f) continue;
dfs_init(v, u);
sz[u] += sz[v];
if (hs[u] == -1 || sz[v] > sz[hs[u]])
hs[u] = v;
}
r[u] = tot;
}
void dfs_solve(int u, int f, bool keep) {
for (auto v : e[u]) {
if (v != f && v != hs[u]) {
dfs_solve(v, u, false);
}
}
if (hs[u] != -1) {
dfs_solve(hs[u], u, true);
}
for (auto v : e[u]) {
if (v != f && v != hs[u]) {
// for (int x = l[v]; x <= r[v]; x++)
// query(id[x]);
for (int x = l[v]; x <= r[v]; x++)
{
if(dep[u]+k>=dep[id[x]])
add(id[x]);
}
}
}
//query(u);
add(u);
del(dep[u]+k+1);
query(u);
if (!keep) {
for(int x = l[u]; x <= r[u]; x++)
del(dep[id[x]]);
}
}
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
cin>>n>>k;
for(int i = 1;i <= n; i++)
cin>>col[i];
for (int i = 1; i < n; i++) {
int u, v; cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs_init(1, 0);
dfs_solve(1, 0, false);
cin>>q;
for(int i = 1;i <= q; i++)
{
int u; cin>>u;
cout<<res[u]<<"\n";
}
return 0;
}
总结
总的来说,没有难题,但关键是细心+读题仔细。会写但不一定写的对,我经常这样,《手比脑子快》。这点要改,以后写之前先理清思路,还要考虑清楚怎么写会好写一点,这点很重要!!!有些写法或许可以,但是复杂,写错的可能性大。这种时候要考虑更好写的写法,比如G题,其实就很简单,但是一开始的写法比较丑。