寒假训练第一周
寒假训练第一周
第一天
蓝桥个人赛
A.带分数
大意:
求数字n有多少种带分数表示方法(每种表示由1~9不重不漏组成)
思路:
暴力搜索
全排列函数next_permutation()
外层while循环是全排列,
内层for循环分配数字给整数部分和分数部分。
代码:
#include <bits/stdc++.h>using namespace std;
#define ll long long#define endl '\n'int a[10];
int work(int l,int r){
int sum=0;
for(int i=l;i<=r;i++){
sum=sum*10+a[i];
}
return sum;
}//验证int t,n;
int main(){
for(int i=0;i<10;i++) a[i]=i+1;
cin>>n;
int ans=0;
do{
for(int i=0;i<7;i++){
int x=work(0,i);
if(x>n) break;
for(int j=i+1;j<8;j++){
int y=work(i+1,j),z=work(j+1,8);
if(y%z==0&&x+y/z==n){
ans++;
}
}
}
}while(next_permutation(a,a+9));
cout<<ans;
return 0;
}
D.灵能传输
大意:
(ai−1,ai,ai+1)←(ai−1+ai,−ai,ai+1+ai)
通过不限次数的操作使ai的最大绝对值最小
思路:
把前缀和用到极致
三个数一次变换后相当于前两个数的前缀和交换,也就是除最后一个数,其他数可以任意排位
加上s[0]=0
按0最小最大~最后一个数的顺序排列
隔一个数依次排
代码:
#include <bits/stdc++.h>using namespace std;
#define endl '\n'#define ll long longint main(){
ll t;
cin>>t;
while(t--) {
ll n;
cin>>n;
vector<ll> pre(n + 1);
for (int i = 1; i <= n; i++) {
cin >> pre[i];
pre[i] += pre[i - 1];
}
ll L = pre[0], R = pre.back();
if (L > R) swap(L, R);
sort(pre.begin(), pre.end());
int l = lower_bound(pre.begin(), pre.end(), L) - pre.begin();
int r = lower_bound(pre.begin() + l + 1, pre.end(), R) - pre.begin();
vector<ll> ve, vr;
vector<bool> vis(n + 1);
for (int i = l; i >= 0; i -= 2) {
ve.push_back(pre[i]);
vis[i] = 1;
}
for (int i = r; i <= n; i += 2) {
vr.push_back(pre[i]);
vis[i] = 1;
}
reverse(vr.begin(), vr.end());
for (int i = 0; i <= n; i++) {
if (!vis[i]) ve.push_back(pre[i]);
}
for (ll i: vr) ve.push_back(i);
ll ans = 0;
for (int i = 0; i < n; i++) {
ans = max(ans, abs(ve[i] - ve[i + 1]));
}
cout << ans << endl;
}
return 0;
}
E.后缀表达式
大意:
给定N个加号,M个减号以及N+M+1个整数,求凑出的合法后缀表达式中最大的结果
思路:
后缀表达式就是可以任意加括号的意思
一、没有减号全加起来
二、1.有减号且全是正数或全是负数,减去一个绝对值最小值
2.有减号且有正有负,所有绝对值相加
代码:
#include <bits/stdc++.h>using namespace std;
#define endl '\n'#define ll long longconst int N=2e5+10;
ll a[N];
int main(){
int n,m;
int ans=0;
ll sum=0,num=0;
cin>>n>>m;
for(int i=1;i<=n+m+1;i++){
cin>>a[i];
if(a[i]<0) ans++;
sum+=abs(a[i]);
num+=a[i];
}
if(m==0) cout<<num<<endl;
else{
sort(a+1,a+m+n+2);
if(ans==0) sum-=2*a[1];
else if(ans==n+m+1) sum+=2*a[n+m+1];
cout<<sum<<endl;
}
return 0;
}
第二天
cf Hello 2023!
B. MKnez's ConstructiveForces Task
大意:
题目大意:给出一整数n,要求输出一个长为n的数组,使得任意两个相邻数的和都等于所有数的和,且数组中不能出现0
思路:
写出来找规律
代码:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int main()
{
ll t;
cin>>t;
while(t--) {
int n;
cin >> n;
if (n == 3)
{
cout << "NO" << endl;
}
else if (n & 1)
{
cout << "YES" << endl;
for (int i = 1; i <= n - 1; i += 2)
{
cout << (n - 1) / 2 - 1 << " " << -(n - 1) / 2 << " ";
}
cout << (n - 1) / 2 - 1 << endl;
}
else
{
cout << "YES" << endl;
for (int i = 1; i <= n; i += 2)
{
cout << "1 -1 ";
}
cout << endl;
}
}
return 0;
}
C. Least Prefix Sum
大意:
给定一个数组,可以对数进行取反操作,先要让s[m]最小,求最少操作次数
思路:
根据前缀和性质
分m后面的数有负数和m前的数有正数的情况去讨论
代码:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int main()
{
ll t;
cin>>t;
while(t--) {
ll n,m;
cin>>n>>m;
ll ans=0;
vector<ll> ve(n+1),pre(n+1);
ll sum=0,suf=0;
for(ll i=1;i<=n;i++){
cin>>ve[i];
pre[i]=pre[i-1]+ve[i];
}
priority_queue<ll> q;
for(ll i=m+1;i<=n;i++){
if(ve[i]<0) q.push(-ve[i]);
while(pre[i]+suf<pre[m]){
suf+=q.top()*2;
q.pop();
ans++;
}
}
while(q.size()) q.pop();
for(ll i=m;i>=1;i--){
if(pre[i]+sum<pre[m]){
sum+=2*q.top();
q.pop();
ans++;
}
if(ve[i]>0) q.push(ve[i]);
}
cout<<ans<<endl;
}
return 0;
}
第三天
中文个人赛
E.狠狠地切割(hard version)
大意:
一个序列设置多个断点,问序列被切割了多少段
思路:
二分查找是否存在断点
一个数不是断点而后一个数是断点则序列+1
注意特判最后一个数,没被切割序列+1
代码:
#include <bits/stdc++.h>using namespace std;
#define ll long longconst ll N=5e5+5;
#define endl '\n'
ll n,m,ans;
ll a[N],b[N];
bool check(ll k){
ll l=1,r=m,mid;
while(l<=r){
mid=(l+r)/2;
if(k<b[mid]) r=mid-1;
else if(k>b[mid]) l=mid+1;
else return 1;
}
return 0;
}
int main(){
cin>>n>>m;
for(ll i=1;i<=n;i++){
cin>>a[i];
}
for(ll i=1;i<=m;i++){
cin>>b[i];
}
sort(b+1,b+1+m);
if(!check(a[n])) ans++;
for(ll i=1;i<n;i++){
if(!check(a[i])&&check(a[i+1])) ans++;
}
cout<<ans;
return 0;
}
G.如何得到npy
大意:
给定一棵 n 个节点的无根树和两个关键点 s,t,
要求对所有边定向,
并计算出每个点到较近的关键点的最小和
思路:
从s和t出发各跑一遍dfs记录到每个点的距离,
最后答案距离取两者中小的那个
输出若两个端点去到同一个终点,则距离较大的立标牌
若去到不同终点,则无需立标牌
代码:
#include <bits/stdc++.h>using namespace std;
#define ll long long#define endl '\n'const ll N=3e5+5;
vector<pair<ll,ll>> g[N];
ll dis1[N],dis2[N],ansdis[N];
vector<pair<ll,ll>> edge;
void dfs(ll u,ll fa,ll dis[]){
for(auto t:g[u]){
ll v=t.first,w=t.second;
if(v==fa) continue;
dis[v]=dis[u]+w;
dfs(v,u,dis);
}
}
int main(){
ll n,s,t;
cin>>n>>s>>t;
ll x,y,z;
for(ll i=1;i<=n-1;i++){
cin>>x>>y>>z;
g[x].push_back({y,z});
g[y].push_back({x,z});
edge.push_back({x,y});
}
dfs(s,0,dis1);
dfs(t,0,dis2);
ll sum=0;
for(ll i=1;i<=n;i++){
ansdis[i]=min(dis1[i],dis2[i]);
sum+=ansdis[i];
}
cout<<sum<<endl;
for(ll i=0;i<n-1;i++){
ll u=edge[i].first,v=edge[i].second;
if(dis1[u]==ansdis[u]&&dis1[v]==ansdis[v]
||dis2[u]==ansdis[u]&&dis2[v]==ansdis[v]){
if(ansdis[u]<ansdis[v]) cout<<2;
else if(ansdis[u]==ansdis[v]) cout<<0;
else cout<<1;
}else cout<<0;
}
return 0;
}
H.做不完的作业
大意:
n个任务有n个所需时间,需依次完成,完成一个任务所需时间连续,每天必须休息,前i天休息时间总和不少于r乘x乘i,求至少需要多少天
思路:
一天时间 − 将要完成的作业时间 + 当前总睡觉时间 +l 天整天睡觉的总时长 ≥ 当前天的 l 天后的要求总睡觉时长
把式子化简,避免出现除法
代码:
#include <bits/stdc++.h>using namespace std;
#define ll long long#define endl '\n'const ll N=3e5+5;
int main(){
ll n,x,p,q;
ll i=1,sum=0,t=0;
cin>>n>>x>>p>>q;
while(n--){
ll w;
cin>>w;
if((x-t-w+sum)*q>=i*p*x&&x-t>w) t+=w;
else{
sum+=x-t;
i++;
long long l=ceil((q*(sum+x-w)-p*i*x)*1.0/(x*p-x*q));
if(l>0){
sum+=x*l;
i+=l;
}
t=w;
}
}
cout<<i;
return 0;
}
第四天
cf ROUND#842(Div.2)
C. Elemental Decompress
大意:
给定一个长度为n的数组a,让构造出两个排列p、q,使得对于数组a的每个元素a[i],让max(p[i],q[i])=a[i],可以输出YES,并输出p和q,否则输出NO
思路:
记录下原数组的数和位置
先排大数
数字出现2次以上buhef
出现2次,让两个排列的不同位置填它,并分别记录下空出的位置
出现1次,让两个排列的相同位置填它
对于未出现的数,塞到空出的位置
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int n,T,a[N];
vector<int>v[N];
int p[N],q[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],v[a[i]].push_back(i);
vector<int>P,Q;
bool fail=0;
for(int i=n;i;i--){
if(v[i].size()>2){
fail=1;
break;
}
if(v[i].size()==2){
p[v[i][0]]=q[v[i][1]]=i;
P.push_back(v[i][0]),Q.push_back(v[i][1]);
}
else if(v[i].size()==1){
p[v[i][0]]=q[v[i][0]]=i;
}
else{
if(!P.size()||!Q.size()){
fail=1;
break;
}
p[Q.back()]=q[P.back()]=i;
P.pop_back(),Q.pop_back();
}
}
if(fail)cout<<"NO\n";
else{
cout<<"YES\n";
for(int i=1;i<=n;i++)cout<<p[i]<<' ';
cout<<'\n';
for(int i=1;i<=n;i++)cout<<q[i]<<' ';
cout<<'\n';
}
for(int i=1;i<=n;i++)v[i].clear();
}
}
第五天
英文个人赛
G.Quests
大意:
有一定量的任务和所对应的奖励,要求在d天内至少拿到c的奖励,问相同任务最多能隔几天再做
思路:
二分答案
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=2e5+5;
ll a[N];
int main()
{
ll t;
cin>>t;
while(t--) {
ll n,c,d;
cin>>n>>c>>d;
for(ll i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
if(a[n]*d<c){
cout<<"Impossible"<<endl;
continue;
}
ll res=0;
for(ll i=1;i<=min(n,d);i++){
res+=a[n-i+1];
if(res>=c) break;
}
if(res>=c){
cout<<"Infinity"<<endl;
continue;
}
ll l=0,r=d+10;
while(l<r){
ll day=0;
ll mid=(l+r+1)/2;
res=0;
ll sum=0;
for(ll i=n;i>=1;i--){
res+=d/(mid+1)*a[i];
day+=d/(mid+1);
if((d%(mid+1))-sum>0){
res+=a[i];
day++;
}
if(day==d) break;
sum++;
}
if(res>=c) l=mid;
else if(res<c) r=mid-1;
}
cout<<l<<endl;
}
return 0;
}
H.SlavicG's Favorite Problem
大意:
一棵树,给定起点终点,每条边上有数值,有一次到任一点(b除外)的传送机会,要求到b时数值为0,对所有走过的边的数值求异或和
思路:
分别从起点和终点跑一遍dfs,记录下到每个点的异或和,若出现相同数值,则成功
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200005;
int n, m, a, b;
int h[maxn], e[maxn], ne[maxn], w[maxn], index;
int ans;
bool flag;
map<int, int> mp;
void add(int x, int y, int z) {
e[index] = y;
w[index] = z;
ne[index] = h[x];
h[x] = index;
index++;
}
void dfsa(int x, int y, int z) {//从起点a开始dfs
mp[z] = 1;//记录每次异或运算的结果,第一次是0
for (int i = h[x]; ~i; i = ne[i]) {
if (e[i] == b || e[i] == y)
continue;
dfsa(e[i], x, z ^ w[i]);
}
}
void dfsb(int x, int y, int z) {//从终点b开始dfs
for (int i = h[x]; ~i ; i = ne[i]) {
if (e[i] == y)
continue;
if (mp[z ^ w[i]]) {
flag = true;
return;
}
dfsb(e[i], x, z ^ w[i]);
}
}
int main() {
int t;
cin >> t;
while (t--) {
index = 0;
mp.clear();
cin >> n >> a >> b;
memset(h, -1, sizeof(h));
for (int i = 0; i < n - 1; i++) {
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
dfsa(a, 0, 0);
flag = false;
dfsb(b, 0, 0);
if (!flag)
cout << "NO" << endl;
else cout << "YES" << endl;
//if (dfsb(b, 0, 0))
// cout << "YES" << endl;
//else
// cout << "NO" << endl;
}
}
I.The Humanoid
大意:给定初始战斗值,2次战斗值乘2的机会,1次战斗值乘3的机会,当战斗值大于敌人时将敌人战斗值除二加到自己的战斗值上,问最多干掉几个敌人
思路:枚举
代码:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
const ll N=2e5+10;
ll a[N];
int main()
{
ll t;
cin>>t;
while(t--){
ll n,h;
cin>>n>>h;
for(ll i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
ll ans=0;
for(ll k=0;k<=2;k++){
ll up=0;
ll i=0;
ll tem=h;
while(i<n){
if(tem>a[i]) tem+=a[i++]/2;
else if(up==3) break;
else{
if(up==k) tem*=3;
else tem*=2;
up++;
}
}
ans=max(ans,i);
}
cout<<ans<<endl;
}
return 0;
}
J. Make It Round
大意:
给定数n和可乘的倍数,求在倍数内最大且后缀0最多的数
思路:
2乘5为10,1乘10为10,
尽量把2,5凑对,
再凑10
代码:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
const ll N=2e5+10;
int main()
{
ll t;
cin>>t;
while(t--){
ll n,m;
cin>>n>>m;
ll cnt2=0,cnt5=0;
ll tem=n;
while(tem%2==0){
cnt2++; tem/=2;
}
tem=n;
while(tem%5==0){
cnt5++; tem/=5;
}
ll x=1;
while(cnt5>cnt2&&x*2<=m){
cnt5--; x*=2;
}
while(cnt5<cnt2&&x*5<=m){
cnt2--; x*=5;
}
while(x*10<=m) x*=10;
cout<<m/x*x*n<<endl;
}
return 0;
}
K. Thermostat
大意:
给定初始温度、温度范围、最少变化量和目标值,
问最少需操作几次达到目标值
思路:
直接考虑最远的边界
代码:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int main()
{
ll t;
cin>>t;
while(t--) {
ll l, r, x, a, b;
cin >> l >> r >> x >> a >> b;
if (a == b){
cout<<0<<endl;
continue;
}else if(abs(a-b)>=x){
cout<<1<<endl;
continue;
}else if(r-max(a,b)>=x||min(a,b)-l>=x){
cout<<2<<endl;
continue;
}else if(r-b>=x&&a-l>=x||r-a>=x&&b-l>=x){
cout<<3<<endl;
continue;
}else cout<<-1<<endl;
}
return 0;
}
第六天
牛客小白月赛65
C.牛牛排队五
大意:
n个人,抽走一些人,问指定人前面一人的序号
思路:
set容器、find()、erase()、prev()
代码:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,k,choice,num;
cin>>n>>k;
vector<int> pre(n+2),nxt(n+2);
for(int i=1;i<=n;i++)
{
pre[i]=i-1;
nxt[i]=i+1;
}
while(k--)
{
cin>>choice>>num;
if(choice==1)
{
nxt[pre[num]]=nxt[num];
pre[nxt[num]]=pre[num];
}
else
cout<<pre[num]<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现