初赛解题报告
补题链接: 2025校赛初赛
A.π
将给定的100位数存到字符串s里面,判断输入的k是否大于100,如果大于输出sry,idk,否则输出
B.It's Mygo!!!!!
第
时间复杂度
C.小Atom的疑惑
只是为了让大家了解校队成员选拔流程而存在的题,按照输入计算每个
训练赛的公式为
校赛的公式为:
需要注意的是这两个的n分别是和40,50取max
最后需要注意的是小Atom是一个很厉害的codeforces蓝名选手,有60分的额外加分
PS:第一个切掉所有小Atom题的小朋友会得到一个Atom玩偶qwq
Fun Fact:Atom本人险些被这道题防了,发生了什么呢?
D.Faker vs Bin
依据题意可得,最终决赛为前4个战队中实力最强,后4个战队中实力最强的pk,实力高者获胜。
void solve()
{
vector<pair<string, int>> v(9);
for (int i = 1; i < 9; ++i)
cin >> v[i].first >> v[i].second;
int f1 = 0, f2 = 0;
for (int i = 1; i <= 4; ++i)
if (v[i].second > v[f1].second)
f1 = i;
for (int i = 5; i <= 8; ++i)
if (v[i].second > v[f2].second)
f2 = i;
if (v[f1].second > v[f2].second)
cout << v[f1].first << " beats " << v[f2].first << endl;
else
cout << v[f2].first << " beats " << v[f1].first << endl;
}
E.移动
只需把每个数的位置记录下来即可,将相差1的数字的间隔累加,跳过间隔最长的两个数。
注意开long long
for(int i = 1; i < n; i++) {
int x = abs(P[i + 1] - P[i]);
sum += x;
Max = max(Max, x);
}
printf("%lld\n", sum - Max);
F.小Atom的养鱼缸
题意:有一块由
题解:本题其实思想就是找对一个点,使得这个高度的时候水不超过给的 $ x$ 值,这个点再多一下下,就会超出给的值,所以我们可以采用二分
什么是二分答案?
答案在一个区间内,当这个区间很大时,暴力超时。但重要的是——这个区间是对题目中的某个量有单调性的,此时,我们就会二分答案。每一次二分会做一次判断,看是否对应的那个量达到/超过了需要的大小。
判断:根据题意写个check函数,如果满足check,就放弃右半区间(或左半区间),如果不满足,就放弃左半区间(或右半区间)。一直往复,直至到最终的答案。
void solve() {
int n, x;
cin >> n >> x;
vector <ll> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
ll l = 0, r = 1ll << 60;
while (r - l > 1) {
ll m = l + r >> 1;
ll tot = 0;
for (int i = 0; i < n; ++i) {
if (a[i] < m) {
tot += m - a[i];
if (tot >= x + 2) {
tot = x + 2;
}
}
}
if (tot <= x) {
l = m;
} else {
r = m;
}
}
cout << l << '\n';
}
另外提供一种不需要二分的做法,先按
int wk(){
sort(a+1,a+n+1);
int now = 0;
for(int i = 1;i <= n; ++i){
if(now + (i-1) * (a[i] - a[i-1]) <= m){
now += (i-1) * (a[i] - a[i-1]);//这是高度达到h所需再添加的水量
}
else{
return (m - now) / (i-1) + a[i-1]; //否则将剩余水量/i-1向下取整即可,i-1是高度低于ai的水柱数量
}
}
return a[n] + (m - now) / n; //同理
}
G.結束バンド(結束乐队)
题意:
你是来亲手结束这个乐队的,M团共有
题解:
是一个很庞大的乐队(
把它抽象成一张图,每个节点是一个乐队成员
首先再额外新建一个节点,表示soyorin要结束乐队的你,你向每个成员连一条边权为
另外,对于一组友谊关系
设
每次在所有边集里找到一端位于V,一端位于
重复该过程
如果时间限制是寻常的,这样看起来就过去了,但是出题人在比赛前一天写题解的时候发现了这件事,所以时间限制就变得不寻常了(但实测能过6个点
该怎么办呢?学一下Prim算法或者Dijkstra算法你就会了
每次查询后更新一下dis数组即可(dis数组是
时间复杂度
H.量子态
根据期望的线性性,所有骰子点数和的期望,就是所有骰子点数期望的和,根据题意分别计算每个骰子的坍缩至各个点数的概率再求期望,最后相加即可
时间复杂度
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
double x;
cin >> x;
ans += x * x * j;
}
}
I.小Atom与奶龙
思路:发现每一行之间修建的代价都互不影响。如果我们算出了每一行的代价,剩下的就是跑一遍前缀和,然后找到代价最小的区间和。因此,问题转化为:给定一行
显然这是一个动态规划问题。假设在位置
在区间
这里提供双向队列的代码
void solve(){
int n,m,k,d;
cin>>n>>m>>k>>d;
int a[n+2][m+2];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
vector<int>ans;
for(int i=1;i<=n;i++){
deque<int>Q;//记录的是序号下标
int dp[m+2];
dp[1]=1;
Q.push_back(1);
for(int j=2;j<=m;j++){
if(!Q.empty()&&j-Q.front()-1>d) Q.pop_front();//间距超出范围,出列
dp[j]=dp[Q.front()]+a[i][j]+1;
while(!Q.empty()&&dp[Q.back()]>dp[j]) Q.pop_back();//后面的比新来的j的成本还要更大,这些数直接不要了
Q.push_back(j);//新来的j入队
}
ans.push_back(dp[m]);
}
int pre[n+2];
pre[0]=ans[0];
for(int i=1;i<ans.size();i++){
pre[i]=pre[i-1]+ans[i];
}
int aans=pre[k-1];
for(int i=k;i<ans.size();i++){
aans=min(aans,pre[i]-pre[i-k]);
}
cout<<aans<<endl;
}
J.X
根据定义,X 本质上有如下几种情况:
同时可以看出
简单讨论一下,假设我们找到最优的
如果:
,那么说明 越大越好,容易推导出此时必有 ,说明 越小越好,同理有
换句话说,本题就是维护每个点的
而这其实是 树的直径DP解法 的扩展,因此也是经典题。
值得注意的是一般的最优解的情况中,不一定是完整的
时间复杂度:
代码:
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define dwn(i,a,b) for(int i = a; i >= b; i--)
#define MAXN 202501
#define int long long
#define inf 99999999
using namespace std;
typedef long long ll;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1; ch = getchar();}
while('0' <= ch && ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int n;
vector<int> v[MAXN];
int a[MAXN],mx[MAXN][4],mn[MAXN][4]; // max,min
void ade(int x,int y){
v[x].push_back(y);
v[y].push_back(x);
}
void getmax(int *x,int y){
if(x[3] < y) x[3] = y;
dwn(i,2,0) if(x[i] < x[i+1]) swap(x[i],x[i+1]);
}
void getmin(int *x,int y){
if(x[3] > y) x[3] = y;
dwn(i,2,0) if(x[i] > x[i+1]) swap(x[i],x[i+1]);
}
void dfs1(int x,int y){
rep(i,0,3) mx[x][i] = -inf, mn[x][i] = inf;
for(auto u : v[x]){
if(u == y) continue;
dfs1(u,x);
getmax(mx[x],max(mx[u][0],a[u]) + a[x]);
getmin(mn[x],min(mn[u][0],a[u]) + a[x]);
}
}
int ans;
void calc(int x){
if(v[x].size() < 2 || mx[x][1] == -inf || mn[x][1] == inf) return ;
ans = max(ans, (a[x] + mx[x][0]) * (a[x] + mx[x][1]));
ans = max(ans, (a[x] + mn[x][0]) * (a[x] + mn[x][1]));
if(v[x].size() == 2 || mx[x][2] == -inf || mn[x][2] == inf) return ;
ans = max(ans, (a[x] + mx[x][0]) * (mx[x][1] + mx[x][2]));
ans = max(ans, (a[x] + mn[x][0]) * (mn[x][1] + mn[x][2]));
if(v[x].size() == 3 || mx[x][3] == -inf || mn[x][3] == inf) return;
ans = max(ans, (mx[x][0] + mx[x][3]) * (mx[x][1] + mx[x][2]));
ans = max(ans, (mn[x][0] + mn[x][3]) * (mn[x][1] + mn[x][2]));
}
void dfs2(int x,int y,int maxx,int minn){
int Mx[4], Mn[4];
rep(i,0,3) Mx[i] = mx[x][i], Mn[i] = mn[x][i];
if(y) getmax(mx[x],maxx + a[x]), getmin(mn[x],minn + a[x]);
calc(x);
for(auto u : v[x]){
if(u == y) continue;
int t1 = mx[x][0], t2 = mn[x][0];
if(t1 == max(mx[u][0],a[u]) + a[x]) t1 = mx[x][1];
if(t2 == min(mn[u][0],a[u]) + a[x]) t2 = mn[x][1];
t1 = max(t1, a[x]); t2 = min(t2, a[x]);
dfs2(u,x,t1,t2);
}
rep(i,0,3) mx[x][i] = Mx[i], mn[x][i] = Mn[i];
}
signed main(){
ans = -inf;
n = read();
rep(i,1,n-1){
int x = read(), y = read();
ade(x,y);
}
rep(i,1,n) a[i] = read();
dfs1(1,0);
dfs2(1,0,0,0);
cout << ans;
return 0;
}