3.11-3.24周报

ZYZZZZ·2024-03-12 23:10·5 次阅读

3.11-3.24周报

寒假训练营2#

D#

这道题的题意很简单,有k张技能牌,每张技能牌可以把前ai张牌放到最下边,消耗bi的花费,现在我们需要的牌在从下往上的第k张,要变到第一张,花费最小的方式。建图的思路就有了,边权就是花费,也就是最短路问题,但是边很灵活,每个点都能建出m条边。

点击查看代码
Copy
void solve(long long kk) { priority_queue<PII,vector<PII>,greater<PII>>q; int n,m,k; cin>>n>>m>>k; for(int i=1;i<=m;i++){ cin>>a[i]>>b[i]; } for(int i=1;i<=n;i++){ dist[i]=LLONG_MAX; } dist[k]=0; q.push({0,k}); while(q.size()){ auto [dis,dian]=q.top(); q.pop(); if(dist[dian]<dis) continue; for(int i=1;i<=m;i++){ int jl=a[i],hf=b[i]; jl+=dian; jl%=n; if(jl==0) jl=n; if(dist[jl]>hf+dis){ dist[jl]=hf+dis; q.push({dist[jl],jl}); } } } if(dist[n]==LLONG_MAX) cout<<-1<<endl; else cout<<dist[n]<<endl; return ; }

codeforces 933 (Div.3)#

E#

这道题的题意很简单,就是每个位置的水深已知,要在两岸之间建桥,那就需要在一些位置建桥柱,桥柱之间的距离不得超过d,要连续建造k座桥,赛时没出也是因为没注意连续。一眼dp但是要注意不能直接暴力求,会超时,但我们发现,每个位置存的花费其实就是前d个里花费最小的和这个点建桥就可以了,那就不用暴力循环,直接求最小。

点击查看代码
Copy
void solve() { ve.clear(); int n,m,K,d; cin>>n>>m>>K>>d; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; } } for(int i=1;i<=n;i++){ //memset(dp,0x3f,sizeof(dp)); multiset<int>s; dp[1]=1; s.insert(dp[1]); for(int j=2;j<=m;j++){ dp[j]=*(s.begin())+a[i][j]+1; //cout<<j<<" "<<j-d-1<<" "<<*s.begin()<<" "<<dp[j]<<endl; if(j-d-1>0){ s.erase(s.find(dp[j-d-1])); } s.insert(dp[j]); // for(int k=j-1;k>0;k--){ // if(j-k-1>d){ // break; // } // dp[j]=min(dp[j],dp[k]+a[i][j]+1); // } } //dp[m]=*s.begin()+a[i][m]+1; ve.push_back(dp[m]); //cout<<i<<" "<<dp[m]<<endl; //cout<<"--------------\n"; } long long cur = 0; for (int i = 0; i < K; i++) cur += ve[i]; long long mn = cur; for (int i = K; i < n; i++) { cur += ve[i] - ve[i - K]; mn = min(cur, mn); } cout << mn << endl; return ; }

天梯1#

7-6#

这题巨简单,但是问题出在了vector的size函数,这个函数的返回值不是int类型,所以要注意可能会出现x<ve.size()-1和x<=ve.size()两个公式不等价的情况。

7-11#

这道题大一就做过,当时就没认真补题,现在遇到还是没思路,其实很简单,根据题意可知,这一定是树,也就是跑完树上的几个点之后不用返回起点,那想要最短路就是遍历所有路径之后减去最长的那条路。找最深的结点就是个dfs,那怎么计算出跑完所有点的路径和呢,那就是返回到当前点回到根节点的路径还有多少路径是没有走过的(因为之前的点可能被其他的点走过了,那就不用重复走了)。

点击查看代码
Copy
int fa[100010]; int vis[100010]; vector<int>g[100010]; int dp[100010]; void dfs(int x){ for(auto e:g[x]){ dp[e]=dp[x]+1; dfs(e); } } void solve(long long kk) { int n,m; cin>>n>>m; int root; for(int i=1;i<=n;i++){ cin>>fa[i]; if(fa[i]==-1){ root=i; } else g[fa[i]].push_back(i); } dp[root]=0; dfs(root); int max1=0; int sum=0; for(int i=1;i<=m;i++){ int x; cin>>x; max1=max(max1,dp[x]); while(vis[x]==0&&x!=root){ vis[x]=1; sum+=2; x=fa[x]; } cout<<sum-max1<<endl; } return ; }

7-12#

这个题其实不难,就是一直不停的套map,因为这个明显也是树,但是可以优化的点是那些老人一定为叶子结点,那我们直接将一个管理机构下的老人数记作这个点的权值,在老人更换机构时就不用考虑边的问题了,直接更换双方的权值即可,求一个管理机构下的老人总数,就是以它为根节点的dfs搜索,权值求和就好,但是有一个段错误,不太懂问题在哪。

点击查看代码
Copy
map<pair<string,string>,int>d; map<string,string>fa; map<string,vector<string>>ve; map<string ,int>xx; int dfs(string s){ int ans=xx[s]; for(auto e:ve[s]){ ans+=dfs(e); //cout<<e<<" "<<s<<" "<<ans<<endl; } return ans; } void solve(long long kk) { int n,m; cin>>n>>m; for(int i=1;i<=m;i++){ string s1,s2; cin>>s1>>s2; fa[s1]=s2; ve[s2].push_back(s1); if(s1[0]>='0'&&s1[0]<='9') xx[s2]++; // cout<<i<<" "<<s2<<" "<<xx[s2]<<endl; // for(auto e:ve[s2]){ // cout<<e<<" "; // } // cout<<"-----\n"; //fa[s1]=s2; } while(1){ string s1; cin>>s1; if(s1=="E"){ break; } if(s1=="T"){ string s2,s3; cin>>s2>>s3; string ss=fa[s2]; xx[ss]--; //cout<<ss<<" "<<s2<<" "<<s3<<endl; ve[s3].push_back(s2); fa[s2]=s3; xx[s3]++; } else if(s1=="Q"){ string s2; cin>>s2; int ans=0; ans=dfs(s2); cout<<ans<<endl; } } return ; }

codeforces 934(Div.2)#

B#

这道题题意很简单,就是把2n个数字(而且是1-n每个数字出现两次)左边一半右边一半,每一半都选k个,要求两边的异或值相同,异或有个规律两个相同的数字异或为0,那就很简单了,先选择出现两次的数字,保证异或值为0,不够就拿只出现一次的数字,只要是出现一次的数字那就是两边都会出现一次,那就都选,异或值还一样,但我细节敲错了,wa了好多次。

点击查看代码
Copy
int a[100010],b[100010]; set<int>s,sa,sb; void solve() { s.clear(); sa.clear(); sb.clear(); int n,k; cin>>n>>k; for(int i=1;i<=n;i++){ cin>>a[i]; sa.insert(a[i]); } for(int i=1;i<=n;i++){ cin>>b[i]; if(sa.count(b[i])){ s.insert(b[i]); sa.erase(b[i]); } else{ sb.insert(b[i]); } } if(sa.size()>=k){ int dans=0; for(auto e:sa){ dans++; cout<<e<<" "<<e<<" "; if(dans==k){ break; } } cout<<endl; dans=0; for(auto e:sb){ dans++; cout<<e<<" "<<e<<" "; if(dans==k){ break; } } cout<<endl; } else { int x=sa.size(); k -= x; for (auto e: sa) { cout << e << " " << e << " "; } int dans = 0; for (auto e: s) { cout << e << " "; dans++; if (dans == 2*k) break; } cout << endl; for (auto e: sb) { cout << e << " " << e << " "; } dans = 0; for (auto e: s) { cout << e << " "; dans++; if (dans == 2*k) break; } cout << endl; } return ; }

天梯训练赛#

I#

这道题就是格雷码的生成,规律很简单,二倍延伸时后一半和前一半轴对称,后一半前一位补1,前一半的前一位补0,然后输出第几个是多少,这个题注意范围很大,要用unsigned long long,要注意后一半的时候因为是反过来的,所以要计算一下它是第几个。

点击查看代码
Copy
#define int unsigned long long #define endl "\n" vector<int>ve; void solve() { int n,k; cin>>n>>k; int d=pow(2,n-1); //cout<<d<<endl; string s; for(int i=1;i<=n;i++){ //cout<<k<<" "<<d<<endl; if(k>=d){ //cout<<k<<" "<<d<<endl; s=s+'1'; k=d-(k-d+1); } else{ s=s+'0'; } d/=2; } cout<<s<<endl; return ; }

J#

这道题已知每个队的实力和初始得分,按照得分排序,12 34 56这样比赛,比完之后实力值更高的得分+1,一看就是结构体排序,但是tle,我们可以注意,每次组好队之后,一定有n/2个队不得分,他们的相对位置不变,胜的同理,这是有顺序的排序,快排会非常浪费,应该使用归并排序,但是要注意,第一次要快排。

点击查看代码
Copy
struct node{ int cj, sl, id; }a[200010],w[200010],l[200010]; bool cmp(node x,node y){ if(x.cj==y.cj){ return x.id<y.id; } return x.cj>y.cj; } void solve(long long kk) { int n,r,q; cin>>n>>r>>q; for(int i=1;i<=2*n;i++){ cin>>a[i].cj; a[i].id=i; } for(int i=1;i<=2*n;i++){ cin>>a[i].sl; } sort(a+1,a+1+2*n,cmp); for(int i=1;i<=r;i++){ int lose=0,win=0; for(int j=1;j<=2*n;j+=2){ if(a[j].sl>a[j+1].sl){ a[j].cj++; w[++win]=a[j]; l[++lose]=a[j+1]; } else{ a[j+1].cj++; w[++win]=a[j+1]; l[++lose]=a[j]; } } merge(w+1,w+1+n,l+1,l+1+n,a+1,cmp); } cout<<a[q].id<<endl; return ; }

Codeforces 935(Div.2)#

D#

这个题蛮好玩的,就是插队,如果你要插某人的队,那你要给它a的钱,从你到这个人之间的所有人你都要给b的钱,其实就是个贪心,但是要注意,到达m之前的位置,不是必须到m

点击查看代码
Copy
int a[200010],b[200010]; void solve() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ cin>>b[i]; } int sum=0; for(int i=n;i>m;i--){ sum+=min(a[i],b[i]); } int sum1=0,min1=LLONG_MAX; for(int i=m;i>0;i--){ min1=min(min1,a[i]+sum1); sum1+=min(a[i],b[i]); } sum+=min1; cout<<sum<<endl; return ; }

E#

这道题题面就是一个二分查找,按照题意的查找方法写二分很快,但有一个点是没有排序,你最多对现有的顺序进行两次交换,使得不按顺序排序的二分查找依然可以找到这个数。当时就有个想法是先看看不交换的话能找到哪个数,然后直接把这两个数交换,其实没想通为什么,但是想试一下就这么写了一下,还真过了。

点击查看代码
Copy
#include <bits/stdc++.h> #define int long long #define endl '\n' using namespace std; int a[200010],b[200010]; void solve() { int n,x; cin>>n>>x; int d; for(int i=1;i<=n;i++){ cin>>a[i]; if(a[i]==x){ d=i; } } int l=1,r=n+1,mid; while(r-l!=1){ mid=(l+r)/2; if(a[mid]<=x){ l=mid; } else{ r=mid; } } if(d==l){ cout<<"0\n"; return ; } cout<<1<<"\n"; cout<<d<<" "<<l<<endl; return ; }
posted @   zyzzzzlh  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示
目录