2025蓝桥杯C++A组省赛 题解
昨天打完蓝桥杯本来想写个 p y t h o n python python A A A 组的题解,结果被队友截胡了。今天上课把 C + + A C++A C++A 组的题看了,感觉挺简单的,所以来水一篇题解。
这场 B B B 是一个爆搜, C C C 利用取余的性质比较好写, D D D 是个猜猜题,当然如果模拟也是可以的, E G EG EG 是数学, F H FH FH 需要预处理。 H H H 是基环树,需要拓扑排序+单调队列维护区间最大值。
p y t h o n python python 和 c + + c++ c++ 相比,我觉得 c + + c++ c++ 组的题要更简单(可能也有我 p y t h o n python python 不太熟悉的原因)。 p y t h o n python python 组全是暴力,我还暴力苦手,最后三题分别是爆搜剪枝,单调栈优化+并查集,反悔贪心。 c + + c++ c++ 组只有 B B B 是暴力,大部分是偏思维和算法,打多了 a c m acm acm 当然会觉得简单一些。
因为没有 O J OJ OJ,所以我也不能保证思路和代码一定正确,仅当作参考,欢迎斧正。
Python A组题解
注:A-G在洛谷测试都没有问题,H有问题!,先别看
A 寻找质数
题面:
第 2025 2025 2025 个质数是多少?
思路:
直接从小到大暴力枚举每一个数,判断是否是素数,到第 2025 2025 2025 个停止就可以了。
n n n 个数里大概有 n ln n \dfrac{n}{\ln n} lnnn 个数是素数, ln n \ln n lnn 大概也就是十几,所以不需要担心越界问题。
code:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;vector<int> primes;
bool check(int n){if(n==1)return false;for(int i=2;i*i<=n;i++)if(n%i==0)return false;return true;
}int main(){for(int i=2;primes.size()<2025;i++)if(check(i))primes.push_back(i);cout<<primes.back();return 0;
}
B 黑白棋
题面:
给你一个 6 × 6 6\times 6 6×6 的棋盘,有的位置一开始就填有棋子,其余格子需要你进行填充,要求:
- 每一行,每一列均只有三个白棋,三个黑棋
- 在某一行或某一列中,不能出现三个同色棋子连续的情况
- 每一行棋子排列方式唯一,与其他行各不相同。同理每一列。行列之间不受限制。
题目保证有唯一解。按照以下格式输出答案:
黑色棋子用 1 1 1 表示,白色棋子用 0 0 0 表示。
从左到右、从上到下的顺序,依次遍历棋盘上的所有格子,并将这些值拼接成一个长度为 36 36 36 的字符串。
思路1:
暴力枚举所有状态,然后判断状态是否成立。
枚举除了已经确定的位置以外的位置的落子情况(总共有 26 26 26 个位置),每个位置只有两种情况(放白棋或者放黑棋),所以总的可能数是 2 26 ≈ 6 × 1 0 8 2^{26}\approx 6\times 10^8 226≈6×108。
枚举到一种状态后,将已经确定的位置的状态加入枚举的状态中。然后判断状态是否成立即可。
总的时间复杂度大概是 总可能数 × \times × (加入已经确定的位置的状态 + + + 判断),后面这个东西的时间复杂度是一个大常数,预估大概也就是 100 100 100。所以能跑动,就是慢一点。
code:
跑了 139 139 139 秒
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
typedef long long ll;void print(string s){for(int i=0;i<6;i++,puts(""))for(int j=0;j<6;j++)cout<<s[i*6+j]<<" ";
}string b2s(ll n){//二进制转string string s;for(ll i=0;i<36;i++)s+="01"[(n>>i)&1];return s;
}
int s2b(string s){//string转二进制ll n=0;for(auto x:s)n=(n<<1)+(x-'0');return n;
}
int idx[]={0,1,3,9,16,17,26,29,31,34};
int colors[]={1,0,0,0,0,0,1,1,0,1};//1,1,1,1,1,1,1,1,1,1
string treat(string s){s=s.substr(0,26);//因为在加入10个固定位置的状态前只有26位 for(int i=0;i<10;i++){int id=idx[i];int c=colors[i];//向id上插入cs=s.substr(0,id)+(c?"1":"0")+s.substr(id);}return s;
}bool check(string s){for(int i=0;i<6;i++){int t=0;for(int j=0;j<6;j++)if(s[i*6+j]=='0')t++;if(t!=3)return false;}for(int j=0;j<6;j++){int t=0;for(int i=0;i<6;i++)if(s[i*6+j]=='0')t++;if(t!=3)return false;}for(int i=0;i<6;i++)for(int j=0;j+2<6;j++)if(s[i*6+j]==s[i*6+j+1] && s[i*6+j+1]==s[i*6+j+2])return false;for(int j=0;j<6;j++)for(int i=0;i+2<6;i++)if(s[i*6+j]==s[(i+1)*6+j] && s[(i+1)*6+j]==s[(i+2)*6+j])return false;set<string> S;for(int i=0;i<6;i++){string t;for(int j=0;j<6;j++)t+=s[i*6+j];S.insert(t);}if(S.size()!=6)return false;S.clear();for(int j=0;j<6;j++){string t;for(int i=0;i<6;i++)t+=s[i*6+j];S.insert(t);}if(S.size()!=6)return false;return true;
}int main(){for(ll i=0;i<(1<<26);i++){string t=treat(b2s(i));if(check(t)){//print(t);cout<<t;//101001010011101100010110011001100110break;}}return 0;
}
/*
1 0 1 0 0 1
0 1 0 0 1 1
1 0 1 1 0 0
0 1 0 1 1 0
0 1 1 0 0 1
1 0 0 1 1 0*/
思路2:
爆搜剪枝
按顺序枚举每个点,尝试落子。然后加点剪枝。
这样搜的好处是可以剪枝,不像思路1需要枚举到所有的状态,因为这个题的要求条件非常苛刻,所以可以狠狠剪枝,所以速度要快很多。
C 抽奖
题意:
抽奖机有三个转轮,每个转轮上有 n n n 个数字图案,标号为 1 ∼ n 1 \sim n 1∼n ,按照从 1 ∼ n 1\sim n 1∼n 顺序转动,当转到第 n n n 个图案时会从第一个继续开始。奖项如下:
- 三个相同的图案,积分 + 200 +200 +200
- 两个相同的图案,积分 + 100 +100 +100
- 三个数字图案,从左到右连续(例如 123 123 123 ),积分 + 200 +200 +200
- 三个数字图案,经过排序后连续,(例如 213 , 321 213, 321 213,321),积分 + 100 +100 +100
抽奖机处于初始状态,三个转轮都处于第一个位置。每次开始抽奖,都会产生三个对应的随机数 x i 1 , x i 2 , x i 3 x_{i1}, x_{i2}, x_{i3} xi1,xi2,xi3 ,表示第 j j j 个转轮会向后转动 x i j x_{i j} xij 次停下。下次抽奖时,转轮会从上一次转动后的位置开始继续转动。
注意,一次抽奖最多只能获得一次积分,如果同时命中多个奖项,以积分最大的那个奖项为准。
请问,如果执行 m m m 次抽奖,总积分值是多少?
n , m ≤ 1 0 3 n,m\le 10^3 n,m≤103, a i , b i , c i ≤ 9 a_i,b_i,c_i\le 9 ai,bi,ci≤9, x i j ≤ 1000 x_{ij}\le 1000 xij≤1000
思路:
把循环和取余联系起来,而不是一个一个数,就会好写很多。
code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e3+5;int n,m;
int a[3][maxn];
int p[3];int main(){cin>>n;for(int i=0;i<3;i++)for(int j=0;j<n;j++)cin>>a[i][j];cin>>m;int ans=0;for(int i=1,t;i<=m;i++){for(int i=0;i<3;i++){cin>>t;p[i]=(p[i]+t)%n;}int x[]={a[0][p[0]],a[1][p[1]],a[2][p[2]]};if(x[0]==x[1] && x[1]==x[2])ans+=200;else if(x[0]+1==x[1] && x[1]+1==x[2])ans+=200;else {if(x[0]>x[1])swap(x[0],x[1]);if(x[1]>x[2])swap(x[1],x[2]);if(x[0]>x[1])swap(x[0],x[1]);if(x[0]==x[1] || x[1]==x[2])ans+=100;if(x[0]+1==x[1] && x[1]+1==x[2])ans+=100;}}cout<<ans<<endl;return 0;
}
/*
4
3 2 4 1
2 2 2 2
4 3 0 9
3
4 4 4
3 1 1
40 39 2*/
D 红黑树
题意:
小蓝最近学习了红黑树,红黑树是一种特殊的二叉树,树上的结点有两种类型:红色结点和黑色结点。
小蓝在脑海中构造出一棵红黑树,构造方式如下:
- 根结点是一个红色结点
- 如果当前结点 c u r N o d e curNode curNode 是红色结点,那么左子结点 c u r N o d e . l e f t curNode.left curNode.left 是红色结点,右子结点 c u r N o d e . r i g h t curNode.right curNode.right 是黑色结点
- 如果当前结点 c u r N o d e curNode curNode 是黑色结点,那么左子结点 c u r N o d e . l e f t curNode.left curNode.left 是黑色结点,右子结点 c u r N o d e . r i g h t curNode.right curNode.right 是红色结点
输入格式:
输入的第一行包含一个正整数 m m m ,表示小蓝挑选的结点数。
接下来 m m m 行,每行包含两个正整数 n i k i n_i\ k_i ni ki ,用一个空格分隔,表示小蓝挑选的结点是第 n i n_i ni 行(从上往下数)第 k i k_i ki 个(从左往右数)结点。
输出格式:
输出 m m m 行,每行包含一个字符串,依次表示小蓝每次挑选的结点的答案。RED
表示红色结点,BLACK
表示黑色结点。
数据范围:
m ≤ 10 m\le 10 m≤10, n i ≤ 30 n_i\le 30 ni≤30, k i ≤ 2 n i − 1 k_i\le 2^{n_i-1} ki≤2ni−1
思路:
其实仔细想想也不算神秘猜猜题。毕竟这是正了八经的完全二叉树的性质。
对一个完全二叉树,从上到下,从左到右进行编号,会有一个性质:对一个节点,编号为 i i i,那么它的左孩子节点编号为 2 i 2i 2i,右孩子节点的编号为 2 i + 1 2i+1 2i+1。
观察一下,你很容易看出来对一个节点,它的左孩子是保持它的颜色,它的右孩子是翻转它的颜色。如果把编号看成二进制数,左孩子编号相当于在它的编号后面加一个 0 0 0,右孩子编号相当于在它的编号后面加一个 1 1 1。这说明了什么?这说明节点颜色和它编号二进制的 1 1 1 的个数有关。
所以你只需要算出第 n n n 行第 k k k 个节点的编号,然后数一下它的二进制中 1 1 1 的个数即可。
再观察一下可以发现第 n n n 行的点只是二进制位上第 n − 1 n-1 n−1 位(从第零位开始算)为 1 1 1,除去最高位 1 1 1,剩余的二进制位组成的数就是 k − 1 k-1 k−1。
code:
#include <iostream>
#include <cstdio>
using namespace std;int count(int x){int cnt=0;while(x){if(x&1)cnt++;x>>=1;}return cnt;
}int n;int main(){cin>>n;for(int i=1,_,k;i<=n;i++){cin>>_>>k;
// cout<<k<<" "<<count(k-1)<<endl;cout<<((count(k-1)%2)?"BLACK":"RED")<<endl;}return 0;
}
/*
2
1 1
2 2*/
E 黑客
题意:
小蓝正在两台电脑之间拷贝数据,数据是一个 n × m n\times m n×m 大小的正整数矩阵,因此总共有 n × m + 2 n\times m+2 n×m+2 个由空格分开的整数,其中前两个整数分别为 n n n 和 m m m。然而,有黑客入侵了小蓝的电脑,导致这 n × m + 2 n\times m+2 n×m+2 个正整数的顺序被打乱了,小蓝想知道最多可能有多少个不同的原矩阵。
两个矩阵相同当且仅当它们行数相同、列数分别相同,且每个位置上的数相同。
输入格式:
输入的第一行包含一个正整数 n × m + 2 n\times m+2 n×m+2 。
第二行包含 n × m + 2 n\times m+2 n×m+2 个正整数 a 1 , a 2 … a n × m + 2 a_1,a_2\dots a_{n\times m+2} a1,a2…an×m+2 ,相邻整数之间使用一个空格分隔。
输出格式:
输出一行包含一个整数表示答案。答案可能很大,请输出答案除以 1000000007
的余数。
数据范围:
n × m + 2 ≤ 5 × 1 0 5 n\times m +2\le 5\times 10^5 n×m+2≤5×105, a i ≤ 5 × 1 0 5 a_i\le 5\times 10^5 ai≤5×105
思路:
首先需要取出两个数作为 n , m n,m n,m。其次这个两个数肯定是 n × m n\times m n×m 的因数(废话)。所以我们可以 O ( n ) O(\sqrt n) O(n) 枚举总数的因数,(如果存在这两个数)去除这两个数,并计算剩余 n × m n\times m n×m 个数的答案。
因为矩阵相同只需要对应位置上的数相同,这个数原本的位置不重要,所以我们可以把所有数按照值统计起来。
如果原本的数各不相同,那么显然总的可能数就是 n m nm nm 个数的全排列,即 A n m n m = ( n m ) ! A_{nm}^{nm}=(nm)! Anmnm=(nm)!。如果其中有 k k k 个数是相等的,我们就需要消除它们之间全排对整体全排列的影响。也就是除序原理。它们 k k k 个数全排列有 k ! k! k! 种可能,这意味着在整体的全排列中会有 k ! k! k! 个其实是一种排列(因为整体在全排时把它们看作是不同的元素),所以需要在原本的基础上除以 k ! k! k!。以此类推,对多个不同的个数大于 1 1 1 的数也都是这样。
假设值为 i i i 的数有 c n t i cnt_i cnti 个,那么答案就是: ( n m ) ! ∏ i c n t i ! \dfrac{(nm)!}{\prod_icnt_i!} ∏icnti!(nm)!
所以再预处理一下阶乘和阶乘倒数即可。
因为 n , m n,m n,m 和 m , n m,n m,n 算两种情况,所以在枚举因数的时候,如果因数 p p p 和 n m / p nm/p nm/p 不一致,答案需要计两遍。
算阶乘可以用 n ! = ( n − 1 ) ! ∗ n n! = (n-1)!*n n!=(n−1)!∗n 递推,算阶乘倒数可以用 1 ( n − 1 ) ! = 1 n ! ∗ n \dfrac{1}{(n-1)!}=\dfrac{1}{n!}*n (n−1)!1=n!1∗n 倒推。
code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=5e5+5;
const int N=5e5;
typedef long long ll;
const ll mod=1e9+7;ll qpow(ll a,ll b){//a^b (%mod)ll ans=1,base=a;while(b){if(b&1)ans=ans*base%mod;base=base*base%mod;b>>=1;}return ans;
}
ll inv(ll x){return qpow(x,mod-2);
}ll nm;
ll cnt[maxn];
ll fac[maxn],ifac[maxn];int main(){fac[0]=1;for(int i=1;i<=N;i++)fac[i]=fac[i-1]*i%mod;ifac[N]=inv(fac[N]);for(int i=N;i>=1;i--)ifac[i-1]=ifac[i]*i%mod;ll ans=0;cin>>nm;for(int i=1,t;i<=nm;i++){cin>>t;cnt[t]++;}nm-=2;for(ll i=1;i*i<=nm;i++){if(nm%i!=0)continue;cnt[i]--;cnt[nm/i]--;if(cnt[i]>=0 && cnt[nm/i]>=0){
// cout<<i<<" "<<nm/i<<endl; ll t=fac[nm];for(int j=1;j<=N;j++)t=t*ifac[cnt[j]]%mod;
// cout<<t<<endl;ans+=t;if(i*i!=nm)ans+=t;ans%=mod;}cnt[i]++;cnt[nm/i]++;}cout<<ans;return 0;
}
/*
6
2 2 1 4 3 3*/
F 好串的数目
题意:
对于一个长度为n 的字符串 s = s 0 s 1 … s n − 1 s=s_0s_1\dots s_{n-1} s=s0s1…sn−1 来说,子串的定义是从中选出
两个下标 l , r l,r l,r ( 0 ≤ l ≤ r ≤ n − 1 0\le l\le r\le n-1 0≤l≤r≤n−1),这之间所有的字符组合起来的一个新的字符串:
s ’ = s l s l + 1 … s r s’ = s_ls_{l+1} \dots s_r s’=slsl+1…sr 就是其中一个子串。
现在给出一个只有数字字符 0 ∼ 9 0\sim9 0∼9 组成的数字字符串,小蓝想要知道在其
所有的子串中,有多少个子串是好串。一个子串是好串,当且仅当它满足以下
两个条件之一:
- 单字符子串一定是好串,即当子串长度为 1 1 1 时,它总是好串;
- 长度大于 1 1 1 时,可以拆分为两个连续非递减子串。
其中,一个串 p = p 0 p 1 … p k − 1 p = p_0p_1\dots p_{k-1} p=p0p1…pk−1 为连续非递减子串是指,对于所有 1 ≤ i < k 1 \le i < k 1≤i<k,满足 p i = p i − 1 p_i=p_{i-1} pi=pi−1 或 p i = p i + 1 + 1 p_i = p_{i+1} + 1 pi=pi+1+1 。即数字串中的每一个数字,要么等于上一个数字,要么等于上一个数字加 1 1 1 。例如 12233 、 456 12233 、456 12233、456 是连续非递减子串。
输入格式:
输入一行包含一个字符串 s s s
输出格式:
输出一行包含一个整数表示答案,即好串的数目。
数据范围:
1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105, s s s 中只包含数字字符 0 ∼ 9 0\sim 9 0∼9
思路:
如果我们把整个串划分成若干段连续非递减子串,比如 122 ‾ 5 ‾ 8 ‾ \underline{122}\underline{5}\underline{8} 12258。只要我们选取的区间不包含超过两个段,那么它就是一个好串。
我们可以枚举左端点,然后快速处理出右端点。其实也就是对一个左端点,我们需要知道左端点所在段的后一个段的右端点在哪里。我们可以预处理这个东西,于是就做完了。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;int n;
string s;
int mir[maxn];int main(){cin>>s;n=s.length();s=" "+s;for(int r=n,l=n+1,lst=n+1;r>=1;r=l-1){//双指针拓展段,每次循环得到一段,段为[l,r]l--;while(l-1>=1 && (s[l]-s[l-1]==1 || s[l]-s[l-1]==0))l--;for(int i=l;i<=r;i++)mir[i]=lst;
// cout<<l<<" "<<r<<" "<<lst<<endl;lst=r+1;}ll ans=0;for(int l=1,r,len;l<=n;l++){//右端点在[l,r)任取 r=mir[l];len=r-l;ans+=len;}cout<<ans;return 0;
}
/*
1225897856*/
G 地雷阵
题意:
小蓝正在平面直角坐标系中的第一象限里玩一个逃生小游戏,在第一象限中埋有 n n n 颗地雷,第 i i i 颗地雷的坐标为 ( x i , y i ) (x_i, y_i) (xi,yi) ,触发范围为以 ( x i , y i ) (x_i, y_i) (xi,yi) 为圆心,半径为 r i r_i ri 的圆。一旦小蓝走进了圆内就会触发地雷导致游戏失败。小蓝初始在原点 ( 0 , 0 ) (0, 0) (0,0) 上,他需要在第一象限内选择一个方向一直往前走,如果能不触发任何地雷即可成功通关游戏。他想知道在 [ 0 , π 2 ] [0,\dfrac{\pi}{2}] [0,2π] 中均匀随机选择一个方向,即在 0 ° 0\degree 0° (朝向 x x x 轴正方向)至 90 ° 90\degree 90°(朝向 y y y 轴正方向)之间随机选择一个方向,通关游戏的概率是多少?
输入格式:
输入的第一行包含一个正整数 n n n 。
接下来 n n n 行,每行包含三个正整数 x i , y i , r i x_i, y_i, r_i xi,yi,ri,相邻整数之间使用一个空格分隔。
输出格式:
输出一行包含一个实数,四舍五入保留三位小数,表示答案。
数据范围:
n ≤ 1 0 5 n\le 10^5 n≤105, x i , x y ≤ 1 0 4 x_i,x_y\le 10^4 xi,xy≤104, r i ≤ m i n ( x i , y i ) r_i\le min(x_i,y_i) ri≤min(xi,yi)
思路:
r i ≤ m i n ( x i , y i ) r_i\le min(x_i,y_i) ri≤min(xi,yi) 说明所有圆不会超出第一象限,可以少一些特殊情况判断。
如果在第一象限有一个圆,它的圆心坐标为 ( x , y ) (x,y) (x,y),半径为 r r r。那么可以得到下图:
不难发现在 [ 0 , π 2 ] [0,\dfrac{\pi}{2}] [0,2π] 中, [ α − θ , α + θ ] [\alpha-\theta,\alpha+\theta] [α−θ,α+θ] 这段是不能取的,而 α = arctan y x , θ = arcsin r R = arcsin r x 2 + y 2 \alpha=\arctan{\dfrac yx},\theta=\arcsin{\dfrac{r}{R}}=\arcsin{\dfrac{r}{\sqrt{x^2+y^2}}} α=arctanxy,θ=arcsinRr=arcsinx2+y2r,其中 R R R 代表从坐标原点到圆心的距离。
n n n 个圆,那么就有 n n n 段不能取的区间。我们取这些区间的并集,假设长度为 l e n len len,那么总长度为 π 2 \dfrac{\pi}{2} 2π,不通关的概率就是 l e n π 2 \dfrac{len}{\dfrac{\pi}{2}} 2πlen,通关概率就是 1 − l e n π 2 1-\dfrac{len}{\dfrac{\pi}{2}} 1−2πlen。
code:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
const double pi=acos(-1);
#define pdd pair<double,double> int n;
vector<pdd> range;int main(){cin>>n;for(int i=1;i<=n;i++){double x,y,r;cin>>x>>y>>r;double alpha=atan(y/x);double theta=asin(r/sqrt(x*x+y*y));range.push_back(pdd(alpha-theta,alpha+theta));}sort(range.begin(),range.end());double p=0,len=0;for(auto [l,r]:range){if(r<p){continue;}else if(l<p){len+=r-p;p=r;}else {len+=r-l;p=r;}}printf("%.3f",1-len/(pi/2));return 0;
}
/*
1
2 2 12
1 3 1
3 1 1*/
H 扫地机器人
题意:
在一个含有 n n n 个点 n n n 条边的无重边无自环的连通无向图中,有一个扫地机器人在执行清扫作业,其中结点 i i i 的标记 t i ∈ { 0 , 1 } t_i \in \{0,1\} ti∈{0,1} 如果为 1 1 1 ,则说明该结点需要进行清扫,扫地机器人在到达这个结点时会顺便进行清扫工作。机器人想知道,如果选定任意结点出发,每条边只能经过一次的话,最多能清扫多少个待清扫结点?
输入格式:
输入的第一行包含一个正整数 n n n 。
第二行包含 n n n 个整数 t 1 , t 2 , … , t n t_1,t_2,\dots,t_n t1,t2,…,tn,相邻整数之间使用一个空格分隔。
接下来 n n n 行,每行包含两个正整数 u i , v i u_i, v_i ui,vi ,用一个空格分隔,表示结点 u i u_i ui 和结点 v i v_i vi 之间有一条边。
输出格式:
输出一行包含一个整数表示答案。
数据范围:
n ≤ 5 × 1 0 5 n\le 5\times 10^5 n≤5×105, t i ∈ { 0 , 1 } t_i \in \{0,1\} ti∈{0,1}, 1 ≤ u i , v i ≤ n 1\le u_i,v_i\le n 1≤ui,vi≤n
思路:
n n n 个点 n n n 条边,还无重边无自环,连通图。这就是一个基环树。
基环树说白了就是一棵树加一条边,于是变成了一种环上的点可以作为树根向外衍生出一棵树的形状。因为无重边无自环,所以环长最小为 3 3 3。
比如这个题的样例对应的图(带圈的表示待清扫结点):
我们的答案一定是从某棵树的树叶开始,走到树根(树根在环上),然后走到另一个树根,再走到这棵树的树叶。比如这个样例的答案路径就是 3 → 1 → 4 → 6 → 7 3\rightarrow 1\rightarrow 4\rightarrow 6\rightarrow 7 3→1→4→6→7。
假设我们把待清理点叫做 1 1 1 点。我们可以处理出以环上某点作为树根,向环外走可达的最大 1 1 1 点个数。于是我们就可以用拓扑排序来删点,边删边算上面说的东西。最后剩一下一个环。
我们可以把这个环展开,展开后有 t n tn tn 个点,按顺序重新编号,假设 a i a_i ai 表示第 i i i 个点是或不是 1 1 1 点, b i b_i bi 表示第 i i i 个点向环外走可达的最大 1 1 1 点个数(也就是上面提到的那个东西),从某个点到另外一个点的答案就是 b i + a i + 1 + a i + 2 + ⋯ + a j − 1 + b j b_i+a_{i+1}+a_{i+2}+\dots+a_{j-1}+b_j bi+ai+1+ai+2+⋯+aj−1+bj。
我们要算答案,就是要枚举左端点 i i i,然后枚举右端点 j j j,计算上面的式子,并取最大值。但是如果我们走环的另一个方向呢?比如样例中的环从 1 → 4 1\rightarrow 4 1→4 也可以是 1 → 5 → 4 1\rightarrow 5\rightarrow 4 1→5→4。其实也好解决,我们将 a , b a,b a,b 数组再循环复制一遍放到后面,这样 i → j i\rightarrow j i→j 的另一个方向就转化为了 j → i + t n j\rightarrow i+tn j→i+tn,这是处理环时的常用策略,不理解可以看下面的图。
不过要注意,当我们枚举 i i i 的时候, j j j 的取值范围是 [ i + 1 , i + t n − 1 ] [i+1,i+tn-1] [i+1,i+tn−1]。否则区间长度大于 t n tn tn 就相当于重复了。
n ≤ 5 × 1 0 5 n\le 5\times 10^5 n≤5×105,怎么快速算呢。稍微处理一下上面的式子: b i + a i + 1 + a i + 2 + ⋯ + a j − 1 + b j = a 1 + ⋯ + a j − 1 + b j − ( a 1 + ⋯ + a i ) + b i \begin{aligned} &b_i+a_{i+1}+a_{i+2}+\dots+a_{j-1}+b_j\\ =&a_1+\dots+a_{j-1}+b_j-(a_1+\dots+a_i)+b_i \end{aligned} =bi+ai+1+ai+2+⋯+aj−1+bja1+⋯+aj−1+bj−(a1+⋯+ai)+bi如果我们枚举左端点 i i i,那么对一个 i i i,其中的 b i b_i bi 和 a 1 + ⋯ + a i a_1+\dots+a_i a1+⋯+ai 就是固定的,我们需要快速确定 j j j,使得上式最大。
我们可以预处理 a a a 的前缀和 s i = a 1 + a 2 + ⋯ + a i s_i=a_1+a_2+\dots+a_i si=a1+a2+⋯+ai。上式转化为 s j − 1 + b j − s i + b i s_{j-1}+b_j-s_i+b_i sj−1+bj−si+bi,其实我们只需要知道 [ i + 1 , i + t n − 1 ] [i+1,i+tn-1] [i+1,i+tn−1] 中 s j − 1 + b j s_{j-1}+b_j sj−1+bj 最大值即可。
可以用单调队列动态维护区间最大值。就是滑动窗口问题。
code:
#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <queue>
using namespace std;
const int maxn=5e5+5;
#define pii pair<int,int>int n;
set<int> g[maxn];
int clean[maxn];vector<int> ring;
int to[maxn];//向环外走可以走到多少1点
void topu(){//拓扑排序找环,并找到环上点向环外可以走到多少1点 queue<int> q;for(int u=1;u<=n;u++)if(g[u].size()==1)q.push(u);while(!q.empty()){int u=q.front();q.pop();for(auto v:g[u]){g[v].erase(u);to[v]=max(to[v],clean[v]+to[u]);if(g[v].size()==1)q.push(v);}g[u].clear();}// for(int u=1;u<=n;u++){
// cout<<u<<":";
// for(auto v:g[u])
// cout<<v<<" ";
// cout<<endl;
// }set<int> S;for(int u=1;u<=n;u++)if(g[u].size()>0)S.insert(u);for(int u=*S.begin(),fa=*g[u].begin();;){ring.push_back(u);for(auto v:g[u]){if(v==fa)continue;if(v==ring[0])return;fa=u;u=v;break;}}
}int main(){cin>>n;for(int i=1;i<=n;i++)cin>>clean[i],to[i]=clean[i];for(int i=1,u,v;i<=n;i++){cin>>u>>v;g[u].insert(v);g[v].insert(u);}topu();int tn=ring.size();vector<int> a(tn*2+5),b(tn*2+5),s(tn*2+5);for(int i=1;i<=tn;i++){a[i]=a[i+tn]=clean[ring[i-1]];b[i]=b[i+tn]=to[ring[i-1]];}for(int i=1;i<=tn*2;i++)s[i]=s[i-1]+a[i];int ans=0;deque<pii> dq;//下标 值 for(int j=1;j<tn;j++){int val=a[j-1]+b[j];while(!dq.empty() && dq.back().second<=val)dq.pop_back();dq.push_back(pii(j,val));}for(int i=1,j=tn,val;i<=tn;i++,j++){//i为左端点,j为右端点 while(!dq.empty() && dq.front().first<=i)dq.pop_front();val=a[j-1]+b[j];while(!dq.empty() && dq.back().second<=val)dq.pop_back();dq.push_back(pii(j,val));int max_val=dq.front().second-a[i]+b[i];ans=max(ans,max_val);}cout<<ans<<endl;return 0;
}
/*
9
1 0 1 0 0 1 1 0 1
2 8
2 9
2 5
1 5
1 3
1 4
4 5
4 6
6 7*/
相关文章:
2025蓝桥杯C++A组省赛 题解
昨天打完蓝桥杯本来想写个 p y t h o n python python A A A 组的题解,结果被队友截胡了。今天上课把 C A CA CA 组的题看了,感觉挺简单的,所以来水一篇题解。 这场 B B B 是一个爆搜, C C C 利用取余的性质比较好写&#…...
用哪个机器学习模型 依靠极少量即时静态数据来训练ai预测足球赛的结果?
目录 一、模型推荐 1.集成树模型(XGBoost/CatBoost) 2.逻辑回归(Logistic Regression) 3.贝叶斯概率模型(Naive Bayes或贝叶斯网络) 4.支持向量机(SVM) 二、模型排除 三、训练…...
讲解贪心算法
贪心算法是一种常用的算法思想,其在解决问题时每一步都做出在当前状态下看起来最优的选择,从而希望最终能够获得全局最优解。C作为一种流行的编程语言,可以很好地应用于贪心算法的实现。下面我们来讲一篇关于C贪心算法的文章。 目录 贪心算法…...
0基础 | 电动汽车的“电源翻译官” | DC/DC转换器 | 电源系统三
你有没有想过,电动汽车里那么多五花八门的电子设备,比如车灯、仪表盘、摄像头,甚至连控制马达的“大脑”(ECU),是怎么用上电的?今天就来聊聊电动车里一个默默工作的“小功臣”——DC/DC转换器&a…...
zynq7020 u-boot 速通
zynq u-boot 速通 简介 上回最小系统已经跑起来,证明串口和 ddr 正确配置.现在我们需要正确配置 网口, qspi, emmc. 网口:通过 tftp 下载 dtb,image,rootfs 在线调试.qspi:固化 boot.bin 到 qspi flash,这样 qspi 启动就可以直接运行 u-boot.emmc:存放 ubuntu_base 跟文件系统…...
C++学习之路,从0到精通的征途:string类的模拟实现
目录 一.string类的成员变量与成员函数 二.string类的接口实现 1.构造函数,析构函数,拷贝构造函数,赋值重载 (1)构造函数 (2)析构函数 (3)拷贝构造函数 &…...
网页制作中的MVC和MVT
MVC(模型-视图-控制器)和MVT(模型-模板-视图)是两种常见的软件架构模式,通常用于Web应用程序的设计。它们之间的主要区别在于各自的组件职责和工作方式。 MVC(模型-视图-控制器): 模…...
02 - spring security基于配置文件及内存的账号密码
spring security基于配置的账号密码 文档 00 - spring security框架使用01 - spring security自定义登录页面 yml文件中配置账号密码 spring:security:user:name: adminpassword: 123456yml文件中配置账号密码后,控制台将不再输出临时密码 基于内存的账号密码 …...
Firebase Studio:开启 AI 驱动的开发新纪元
Firebase Studio(前身为 Project IDX)的推出,标志着软件开发范式正经历深刻变革。它不仅是一个传统的 IDE,更是一个以 AI 为主导的、代理式 (agentic) 的云端开发环境,专注于全栈 AI 应用(包括 API、后端、…...
网络基础2
目录 跨网络传输流程 网络中的地址管理 - 认识 IP 地址 跨网络传输 报文信息的跨网络发送 IP地址的转化 认识端口号 端口号范围划分 源端口号和目的端口号 认识 TCP / UDP协议 理解 socket 网络字节序 socket 编程接口 sockaddr 结构 我们继续来学习网络基础 跨网…...
Maven工具学习使用(十一)——部署项目到仓库
1、使用Maven默认方式 Maven 部署项目时默认使用的上传文件方式是通过 HTTP/HTTPS 协议。要在 Maven 项目中配置部署,您需要在项目的 pom.xml 文件中添加 部分。这个部分定义了如何部署项目的构件(如 JAR 文件)到仓库。。这个部分定义了如何…...
FPGA 37 ,FPGA千兆以太网设计实战:RGMII接口时序实现全解析( RGMII接口时序设计,RGMII~GMII,GMII~RGMII 接口转换 )
目录 前言 一、设计流程 1.1 需求理解 1.2 模块划分 1.3 测试验证 二、模块分工 2.1 RGMII→GMII(接收方向,rgmii_rx 模块) 2.2 GMII→RGMII(发送方向,rgmii_tx 模块) 三、代码实现 3.1 顶层模块 …...
torch.cat和torch.stack的区别
torch.cat 和 torch.stack 是 PyTorch 中用于组合张量的两个常用函数,它们的核心区别在于输入张量的维度和输出张量的维度变化。以下是详细对比: 1. torch.cat (Concatenate) 作用:沿现有维度拼接多个张量,不创建新维度 输入要求…...
索引下推(Index Condition Pushdown, ICP)
概念 索引下推是一种数据库查询优化技术,通过在存储引擎层面应用部分WHERE条件来减少不必要的数据读取。它特别适用于复合索引的情况,因为它可以在索引扫描阶段就排除不符合全部条件的数据行,而不是将所有可能匹配的记录加载到服务器层再进行…...
C++基础精讲-06
文章目录 1. this指针1.1 this指针的概念1.2 this指针的使用 2. 特殊的数据成员2.1 常量数据成员2.2 引用数据成员2.3 静态数据成员2.4 对象成员 3. 特殊的成员函数3.1 静态成员函数3.2 const成员函数3.3 mutable关键字 1. this指针 1.1 this指针的概念 1.c规定,t…...
Django3 - 建站基础
学习开发网站必须了解网站的组成部分、网站类型、运行原理和开发流程。使用Django开发网站必须掌握Django的基本操作,比如创建项目、使用Django的操作指令以及开发过程中的调试方法。 一、网站的定义及组成 网站(Website)是指在因特网上根据一定的规则,…...
UE5蓝图设置界面尺寸大小
UE5蓝图设置界面尺寸大小 Create widget 创建UIadd to Viewport 添加视图get Game User Settings获取游戏用户设置set Screen Resolutions 设置屏幕尺寸大小1920*1080set Fullscreen Mode 设置全屏模式为:窗口化或者全屏Apply Settings 应用设置...
无数字字母RCE
无数字字母RCE,这是一个老生常谈的问题,就是不利用数字和字母构造出webshell,从而能够执行我们的命令。 <?php highlight_file(__FILE__); $code $_GET[code]; if(preg_match("/[A-Za-z0-9]/",$code)){die("hacker!&quo…...
AutoGen参数说明
UserProxyAgent用户 user_proxy = UserProxyAgent配置说明: # 构造参数 def __init__(self,name: str,is_termination_msg: Optional[Callable[[Dict], bool]] = None,max_consecutive_auto_reply: Optional[int] = None,human_input_mode: Literal["ALWAYS", &qu…...
6.2 GitHub API接口设计实战:突破限流+智能缓存实现10K+仓库同步
GitHub Sentinel 定期更新 API 接口设计 关键词:GitHub API 集成、异步爬虫开发、RESTful 接口设计、请求限流策略、数据增量更新 1. 接口架构设计原则 采用 分层隔离架构 实现数据采集与业务逻辑解耦: #mermaid-svg-WihvC78J0F5oGDbs {font-family:"trebuchet ms&quo…...
用java代码如何存取数据库的blob字段
一.业务 在业务中我们被要求将文件或图片等转成 byte[] 或 InputStream存到数据库的Blob类型的字段中. 二.Blob类型介绍 在 MySQL 中,Blob 数据类型用于存储二进制数据。MySQL 提供了四种不同的 Blob 类型: TINYBLOB: 最大存储长度为 255 个字节。BL…...
2025蓝桥杯C++研究生组真题-上海市省赛
2025蓝桥杯C研究生组真题 A:数位倍数(5分) 问题描述:请问在 1 至 202504(含)中,有多少个数的各个数位之和是 5 的整数倍。例如:5、19、8025 都是这样的数。 A是填空题,…...
原子操作CAS(Compare-And-Swap)和锁
目录 原子操作 优缺点 锁 互斥锁(Mutex) 自旋锁(Spin Lock) 原子性 单核单CPU 多核多CPU 存储体系结构 缓存一致性 写传播(Write Propagation) 事务串行化(Transaction Serialization&#…...
Aspose.Words导出word,服务器用内存流处理,不生成磁盘文件
框架集:.NET8 public async Task<IActionResult> ExportPDF(long? id) {var infoawait form_Dahui_ReportDao.GetAsync(id);if (info null){return Content("没找到数据");}//读取word模板string fileTemp Path.Combine(AppContext.BaseDirect…...
攻防世界——Web题ez_curl
目录 Express PHP和Node.js的解析差异 Python代码 这道题最终得不到flag,用了很多师傅的代码也不成功。但还是需要学习 下载的附件: const express require(express);const app express();const port 3000; const flag process.env.flag;app.ge…...
力扣面试150题--螺旋矩阵
Day 20 题目描述 思路 根据题目描述,我们需要顺时针输出矩阵元素,顺时针说明有四种输出状态,横向从左到右和从右到左,纵向从上到下和从下到上,唯一的难点在于,输出完成一层后,如何进入内层&am…...
智能指针之设计模式2
前面介绍了工厂模式控制了智能指针和资源对象的创建过程,现在介绍一下智能指针是如何利用代理模式来实现“类指针(like-pointer)”的功能,并控制资源对象的销毁过程的。 2、代理模式 代理模式是为其它对象提供一种代理以控制对这…...
【Redis】redis持久化
Redis 持久化 Redis:非关系型的内存数据库 持久化:将数据永久写入磁盘(内存→磁盘) Redis 默认开启了持久化,默认模式为 RDB 为什么需要持久化? Redis 是内存数据库,宕机或关机后数据会丢失。…...
AtCoder Beginner Contest 401 E题 题解
E - Reachable Sethttp://E - Reachable Set 题意概述 : 给定一个无向图, 对于每个 ,解决以下问题: -选择最少的一些顶点,使得删除这些顶点及其关联的所有边后 点1只能到达以内的所有点 牵制芝士 :头文…...
Kubernetes控制平面组件:API Server Webhook 授权机制 详解
云原生学习路线导航页(持续更新中) kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计(一)Kubernetes架构原则和对象设计(二)Kubernetes架构原则和对象设计(三)Kubernetes控…...
【CodeMirror】系列(二)官网示例(六)自动补全、边栏
一、自动补全 codemirror/autocomplete 包提供了在编辑器中显示输入建议的功能。这个示例展示了如何启用该功能以及如何编写自己的补全来源。 自动补全是通过在编辑器的配置项中加入 autocompletion 扩展实现的。有些语言包支持内置的自动补全功能,比如HTML包。 默…...
CSS 表格样式学习笔记
CSS 提供了强大的工具来美化和定制 HTML 表格的外观。通过合理使用 CSS 属性,可以使表格更加美观、易读且功能强大。以下是对 CSS 表格样式的详细学习笔记。 一、表格边框 1. 单独边框 默认情况下,表格的 <table>、<th> 和 <td> 元…...
简单记录一下Android四大组件
1、Android Layout 1.1、LinearLayout 线性布局,子控件按照水平或垂直的方向依次排列,排列方向通过属性android:orientation控制,horizontal为水平排列,vertical为垂直排列。对于同一水平线上的控件,可以调整它的lay…...
在线地图支持天地图和腾讯地图,仪表板和数据大屏支持发布功能,DataEase开源BI工具v2.10.7 LTS版本发布
2025年4月11日,人人可用的开源BI工具DataEase正式发布v2.10.7 LTS版本。 这一版本的功能变动包括:数据源方面,Oracle数据源支持获取和查询物化视图;图表方面,在线地图支持天地图、腾讯地图;新增子弹图&…...
【图像处理基石】什么是通透感?
一、画面的通透感定义 画面的通透感指图像在色彩鲜明度、空间层次感、物体轮廓清晰度三方面的综合表现,具体表现为: 色彩鲜明:颜色纯净且饱和度适中,无灰暗或浑浊感;层次分明:明暗过渡自然,光…...
猫咪如厕检测与分类识别系统系列【六】分类模型训练+混合检测分类+未知目标自动更新
前情提要 家里养了三只猫咪,其中一只布偶猫经常出入厕所。但因为平时忙于学业,没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关,频繁如厕可能是泌尿问题,停留过久也可能是便秘或不适。为了更科学地了解牠的如…...
NoSQL入门指南:Redis与MongoDB的Java实战
一、为什么需要NoSQL? 在传统SQL数据库中,数据必须严格遵循预定义的表结构,就像把所有物品整齐摆放在固定尺寸的货架上。而NoSQL(Not Only SQL)数据库则像一个灵活的储物间,允许存储各种类型的数据&#x…...
游戏引擎学习第223天
回顾 今天我们正在进行过场动画序列的制作,因此我想深入探讨这个部分。昨天,我们暂时停止了过场动画的制作,距离最终结局还有一些内容没有完成。今天的目标是继续完成这些内容。 我们已经制作了一个过场动画的系列,并把它们集中…...
【redis进阶二】分布式系统之主从复制结构(1)
目录 一 为什么要有分布式系统? 二 分布式系统涉及到的非常关键的问题:单点问题 三 学习部署主从结构的redis (1)创建一个目录 (2)进入目录拷贝两份原有redis (3)使用vim修改几个选项 (4)启动两个从节点服务器 (5)建立复制,要想配…...
(自用)若依生成左树右表
第一步: 在数据库创建树表和单表: SQL命令: 商品表 CREATE TABLE products (product_id INT AUTO_INCREMENT PRIMARY KEY,product_name VARCHAR(255) , price DECIMAL(10, 2) , stock INT NOT NULL, category_id INT NOT NULL); 商品分类…...
VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略
VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略 本教程专为中高级开发者设计,系统讲解VectorBT技术在量化交易中的应用。通过结合Tushare数据源和TA-Lib技术指标,深度探索策略开发、回测优化与风险评估的核心方法…...
5G网络下客户端数据业务掉线频繁
MCPTT(Mission Critical Push-to-Talk)客户端的日志,和界面在待机状态下(即没有做通话等业务操作),会频繁提示“离线”。 主要先看有没有丢网,UL BLER有没有问题。确认没有问题。看到业务信道释…...
CPU(中央处理器)
一、CPU的定义与核心作用 CPU 是计算机的核心部件,负责 解释并执行指令、协调各硬件资源 以及 完成数据处理,其性能直接影响计算机的整体效率。 核心功能: 从内存中读取指令并译码。执行算术逻辑运算。控制数据在寄存器、内存和I/O设备间的…...
Java从入门到“放弃”(精通)之旅——程序逻辑控制④
Java从入门到“放弃”(精通)之旅🚀:程序逻辑的完美理解 一、开篇:程序员的"人生选择" 曾经的我,生活就像一段顺序执行的代码: System.out.println("早上8:00起床"); Syste…...
[Dify] 基于明道云实现金融业务中的Confirmation生成功能
在金融业务的日常流程中,交易记录的处理不仅涉及数据录入、流程审批,更重要的是其最终输出形式——交易确认函(Confirmation)。本文将介绍如何通过明道云的打印模板功能,快速、准确地生成符合业务需求的交易Confirmation,提升工作效率与合规性。 为什么需要Confirmation?…...
Qt安卓设备上怎么安装两个不同的Qt应用?
在安卓设备上安装两个不同的Qt应用时,需要确保这两个应用在安卓系统中被视为独立的应用程序。以下是详细的步骤和注意事项,帮助你实现这一目标: 一、修改应用的包名 安卓系统通过应用的包名(package属性)来区分不同的…...
Prompt工程提示词(1-6章)
White graces:个人主页 🐹今日诗词:怅望千秋一洒泪,萧条异代不同时🐹 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏 目录 🚀 第…...
0基础 | 硬件滤波 C、RC、LC、π型
一、滤波概念 (一)滤波定义 滤波是将信号中特定波段频率滤除的操作,是抑制和防止干扰的重要措施。通过滤波器实现对特定频率成分的筛选,确保目标信号的纯净度,提升系统稳定性。 (二)滤波器分…...
C++ 编程指南34 - C++ 中 ABI 不兼容的典型情形
一:概述 ABI(Application Binary Interface)是二进制层面的接口规范。如果一个库的 ABI 发生了变化,那么基于旧 ABI 编译的代码可能在运行时与新库不兼容(即使接口名字都一样也不行)。那么在C++中编程中,哪些情形会导致ABI不兼容呢?下面逐一列举一下。 二:C++ 中 ABI…...
【动态规划】深入动态规划:背包问题
文章目录 前言01背包例题一、01背包二、分割等和子集三、目标和四、最后一块石头的重量|| 完全背包例题一、完全背包二、 零钱兑换三、零钱兑换||四、完全平方数 前言 什么是背包问题,怎么解决算法中的背包问题呢? 背包问题 (Knapsack problem) 是⼀种组…...