2023年天梯赛选拔-部落划分
Posted xjwrr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2023年天梯赛选拔-部落划分相关的知识,希望对你有一定的参考价值。
题目链接:https://www.luogu.com.cn/problem/P4047
感受:比赛时秒认为是二分,但二分的细节很多且不好处理,但我就是要二分。
主要死在对并查集还不够熟悉,本题二分判断有几个部落只能通过并查集来实现,普通的模拟无法实现。
思路:对答案进行二分,当算出来的联通的数目大于题目给定的,说明二分的答案偏小。反之答案偏大。
这里说一下当两点间的距离与二分的答案相等的情况,如本题样例,当二分答案为0.9999时,联通块数目是4,当二分答案为1时,连通块数目是1,并没有二分到k,我们只需要去掉二分答案为1时的边的长度为1的边,这样联通块的数目就与题目给定的相等了。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
typedef pair<int,int>P;
int p[N];
P edge[N];
int n,k;
int find(int x)
if (x!=p[x]) p[x] = find(p[x]);
return p[x];
double get(int x1,int y1,int x2,int y2)
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
bool check(double mid)
int cnt = 0;
for (int i=1;i<=n;i++) p[i] = i;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
double d = get(edge[i].first,edge[i].second,edge[j].first,edge[j].second);
if (d<=mid)
int X = find(i),Y = find(j);
p[X] = Y;
for (int i=1;i<=n;i++)
if (p[i]==i) cnt++;
if (cnt>=k) return 1;
return 0;
int main()
cin>>n>>k;
for (int i=1;i<=n;i++) cin>>edge[i].first>>edge[i].second;
double l = 0,r = 20000;
while(r-l>1e-9)
double mid = (l+r)/2;
if (check(mid)) l = mid;
else r = mid;
printf("%.2f",l);
return 0;
2022年天梯赛上海理工大学校内选拔赛部分题 题解
题目地址:https://ac.nowcoder.com/acm/contest/30532
题目难度偏简单,做出了7道快8道,认真做可能估计8-9道左右。
目录
- A+B Problem【签到】
- Komorebi的数学课【快速幂板子】
- 次佛锅【哈希表 字符串处理】
- Setsuna的K数列【k进制 思维】
- Wiki下象棋【BFS】
- 黄金律法【贪心】
- 天气预报【双指针】
- 叠硬币【DP】
- A+B Problem again【trie树】
- 史东薇尔城【最短路】
- 剪绳子【思维】
- 数硬币【线段树 区间加 区间查询最大公约数 区间求和】
A+B Problem【签到】
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N],n;
int main(void)
cin>>n;
for(int i=0;i<n;i++) cin>>a[i],b[i]=a[i];
sort(a,a+n);
for(int i=0;i<n;i++)
if(b[i]==a[n-1]) cout<<b[i]+a[n-2]<<" "; //是最大值,则加倒数第二数
else cout<<b[i]+a[n-1]<<" ";
return 0;
Komorebi的数学课【快速幂板子】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL n;
LL quick(LL a,LL b,LL p)
LL sum=1;
while(b)
if(b&1) sum=sum*a%p;
b>>=1;
a=a*a%p;
return sum%p;
int main(void)
cin>>n;
cout<<quick(n,n,n+2)<<endl;
return 0;
次佛锅【哈希表 字符串处理】
关键点在于处理,第一行的数据。
#include<bits/stdc++.h>
using namespace std;
string a,b;
map<string,int>mp;
vector<string>ve;
int main(void)
getline(cin,a);
stringstream l(a);
int cnt=0;
while(l>>b)
cnt++;
ve.push_back(b);
if(cnt==2)//说明一个名字 一个数字了
mp[ve[0]]+=stoi(ve[1]);
ve.clear();
cnt=0;
int n; cin>>n;
while(n--)
cin>>a;
cout<<mp[a]<<endl;
return 0;
Setsuna的K数列【k进制 思维】
就是k进制,之前见过类似的题目。关键在于读懂题。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int mod=1e9+7;
LL n,f[100],k;
vector<LL>ve;
int main(void)
cin>>n>>k;
for(LL i=0,j=1;i<=31;i++,j=j*k%mod) f[i]=j%mod;
LL sum=0;
for(int i=31;i>=0;i--)
if(n>>i&1) sum=(sum+f[i])%mod;
cout<<sum;
return 0;
Wiki下象棋【BFS】
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long int LL;
const int N=510;
int t,n,m,k,a,b,c,d;
int s[N][N],st[N][N],dist[N][N];
int bfs(int x,int y,int w)
memset(st,0,sizeof st);
memset(dist,0x3f,sizeof dist);
queue<PII>q; q.push(a,b); st[a][b]=1; dist[a][b]=0;
int dx[8]=;
while(q.size())
auto temp=q.front(); q.pop();
int x=temp.first,y=temp.second;
if(x==c&&y==d) return dist[x][y];
if(w==1)
int dx[8]=-2,-2,1,1,-1,-1,2,2;
int dy[8]=-1,1,-2,2,-2,2,-1,1;
for(int i=0;i<8;i++)
int tempx=x+dx[i],tempy=y+dy[i];
if(tempx<1||tempx>n||tempy<1||tempy>m) continue;
if(st[tempx][tempy]) continue;
if(s[tempx][tempy]) continue;
st[tempx][tempy]=1;
dist[tempx][tempy]=dist[x][y]+1;
q.push(tempx,tempy);
else
int dx[8]=-2,-2,1,1,-1,-1,2,2;
int dy[8]=-1,1,-2,2,-2,2,-1,1;
int dx1[8]=-1,-1,0,0,0,0,1,1;//马脚
int dy1[8]=0,0,-1,1,-1,1,0,0;//马脚
for(int i=0;i<8;i++)
int tempx=x+dx[i],tempy=y+dy[i];
if(tempx<1||tempx>n||tempy<1||tempy>m) continue;
if(st[tempx][tempy]) continue;
if(s[tempx][tempy]) continue;
if(s[x+dx1[i]][y+dy1[i]]) continue;//憋马脚了
st[tempx][tempy]=1;
dist[tempx][tempy]=dist[x][y]+1;
q.push(tempx,tempy);
return -1;
int main(void)
cin>>t;
while(t--)
scanf("%d%d%d%d%d%d%d",&n,&m,&k,&a,&b,&c,&d);
memset(s,0,sizeof s);
while(k--)
int x,y; scanf("%d%d",&x,&y);
s[x][y]++;
int ans1=bfs(a,b,1);
int ans2=bfs(a,b,2);
printf("%d %d\\n",ans1,ans2);
return 0;
黄金律法【贪心】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e6+10;
int t,n,a[N],b[N];
bool cmp(int a,int b)return a>b;
int main(void)
cin>>t;
while(t--)
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
LL sum=0;
sort(a+1,a+n+1);
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++) sum+=1ll*a[i]*b[i];
cout<<sum<<endl;
return 0;
天气预报【双指针】
很容易的分析出,它是有单调性的。即当右边界满足的时候,后面的也一定满足。
故可以双指针来维护,需要注意的是 a==0 b==0
这种情况,还需额外加1。因为题目说了可以啥都不选。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef unsigned long long int LL;
LL n,a,b,s[N];
string c;
bool check(int i,int j)
int l=s[j]-s[i-1];
int r=(j-i+1)-l;
if(l>=b&&r>=a) return true;
return false;
int main(void)
cin>>n>>a>>b>>c;
c="*"+c;
for(int i=1;i<=n;i++) s[i]=s[i-1]+c[i]-'0';
long long int ans=0;
for(int i=1,j=1;i<=n;i++)
while(j<=n&&!check(i,j)) j++;
while(j<=n&&j<i) j++;
if(j<=n&&check(i,j)) ans+=(n-j+1);
if(!a&&!b) ans++;
cout<<ans;
return 0;
叠硬币【DP】
这道还是挺有意思的,难度就在于输出,以及字典序最小输出。
我们可以从大到小排序。这样的话它会一直的覆盖,此时最后的就是做小的字典序。
存前驱结点可以用一个数组来存。
#include<bits/stdc++.h>
using namespace std;
const int N=3010;
int f[N],a[N],pre[N],n,m; //pre[i] 表示放到i高时的 最后一个硬币的高度
bool cmp(int a,int b)return a>b;
int main(void)
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1,cmp);
for(int i=0;i<N;i++) f[i]=1e9;
f[0]=0,pre[0]=0;
for(int i=1;i<=n;i++)
for(int j=m;j>=a[i];j--)
if(f[j]>f[j-a[i]]+1 || (f[j] == f[j-a[i]]+1 && pre[j] > a[i]))
f[j] =以上是关于2023年天梯赛选拔-部落划分的主要内容,如果未能解决你的问题,请参考以下文章
牛客 2022年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛 签到题13题