2019-2020 ACM-ICPC Brazil Subregional Programming Contest
A、给你一堆圆,问你能否从左上角走到右下角不经过所有圆
一种经典套路题,考虑使用并查集判联通,如果两圆相交,就把他们合并,如果圆与边界相交则合并圆与边界
最后判断一下边界能否互相抵达即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=10005;
const double eps=1e-9;
int n,m,k;
struct node{
double x,y,r;
}o[N];
int f[N];
int _find(int x){
if(f[x]!=x) f[x]=_find(f[x]);
return f[x];
}
void _merge(int x,int y){
x=_find(x),y=_find(y);
if(x!=y) f[x]=y;
}
double dis(node a,node b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool smlepl(double a,double b){
if(a<b||fabs(a-b)<eps) return 1;
return 0;
}
void gao(){
for(int i=1;i<=k+4;++i)
f[i]=i;
int x0=k+1,y0=k+2,xm=k+3,yn=k+4;
for(int i=1;i<=k;++i){
for(int j=1;j<=k;++j){
if(i==j) continue;
if(smlepl(dis(o[i],o[j]),o[i].r+o[j].r))
_merge(i,j);
}
if(o[i].y-o[i].r<=0) _merge(i,y0);
if(o[i].y+o[i].r>=n) _merge(i,yn);
if(o[i].x-o[i].r<=0) _merge(i,x0);
if(o[i].x+o[i].r>=m) _merge(i,xm);
}
if(_find(y0)==_find(yn)||_find(x0)==_find(xm)||
_find(x0)==_find(y0)||_find(xm)==_find(yn))
puts("N");
else puts("S");
}
int main(){
scanf("%d%d%d",&m,&n,&k);
for(int i=1;i<=k;++i)
scanf("%lf%lf%lf",&o[i].x,&o[i].y,&o[i].r);
gao();
return 0;
}
B、签到,判断第一个数是不是最大的那一个
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,a[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
int mx=-0x3f3f3f3f;
int pos=-1;
for(int i=1;i<=n;++i){
if(a[i]>mx){
mx=a[i];
pos=i;
}
}
if(pos==1) puts("S");
else puts("N");
return 0;
}
D、给定一棵树,你需要选择k个叶子,标记从这些叶子到根节点路径上的所有节点
问最多有几个节点被标记
考虑长链剖分,用树链剖分的套路,详见:树链剖分
选出最长的k条链即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k;
int f[N],dep[N],len[N],son[N],top[N];
vector<int> e[N];
vector<int> tmp;
void d1(int x){
len[x]=1;
for(auto y:e[x]){
dep[y]=dep[x]+1;
d1(y);
len[x]=max(len[x],len[y]+1);
if(len[y]>len[son[x]]) son[x]=y;
}
}
void d2(int x,int t){
top[x]=t;
if(e[x].size()==0){
tmp.push_back(dep[x]-dep[top[x]]+1);
return;
}
if(son[x]) d2(son[x],t);
for(auto y:e[x]){
if(y!=son[x]) d2(y,y);
}
}
void gao(){
int leaf=0;
for(int i=1;i<=n;++i)
if(e[i].size()==0) leaf++;
if(k>=leaf){
printf("%d",n);
return;
}
dep[1]=1;
d1(1);
d2(1,1);
sort(tmp.begin(),tmp.end(),greater<int>());
int ans=0;
for(int i=0;i<k;++i) ans+=tmp[i];
printf("%d",ans);
}
int main(){
scanf("%d%d",&n,&k);
f[1]=0;
for(int i=2;i<=n;++i)
scanf("%d",&f[i]),e[f[i]].push_back(i);
gao();
return 0;
}
/*
9 2
1 1 3 2 5 6 7 7
*/
H、有个人在跑步,给定每圈的标志个数和他总路程的圈数,问你他在完成10%、20%......90%路程之后经过了几个标志
直接算即可,记得向上取整
代码:
#include <bits/stdc++.h>
using namespace std;
int v,n;
int main(){
scanf("%d%d",&v,&n);
for(int i=1;i<=9;i++){
printf("%d ",((v*n*i)+9)/10);
}
return 0;
}
L、题意比较怪,说的是两个人玩硬币,这是一枚不均衡的硬币,为了保证游戏公平,把这个硬币投掷n次,不同的情况对应着不同的人的胜利
问你把所有的情况分配给两个人,在保证两人获胜概率均等的情况下,最多能分配多少种情况
考虑什么情况下是获胜概率相等的两种情况,显然是1和0的数量相等的两种情况出现概率相同
打表找规律,发现规律就是2的(n的二进制表示下1的个数)次方
这里被__builtin_popcount()坑死了,因为它只能计算int中的1的个数
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int gao(ll n){
int ans=0;
while(n){
if(n&1) ans++;
n/=2;
}
return ans;
}
int main(){
cin>>n;
cout<<(1ll<<gao(n));
return 0;
}
M、c个人和n个任务,每个人会分到连续的一段任务,单位时间一个人可以完成t个任务,问完成任务的最短时间是多少
二分时间即可,贪心地分配任务,能完成就尽量完成,否则分配给下一个人,最后看需要的人数是否小于c
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n,c,t;
int a[N];
bool chk(ll now){
int cnt=0,sum=0;
for(int i=1;i<=n;++i){
if(a[i]>now*t) return 0;
if(sum+a[i]<=now*t)
sum+=a[i];
else{
cnt++;
sum=a[i];
}
}
if(sum!=0) cnt++;
return cnt<=c;
}
ll half(ll l,ll r){
while(l<=r){
ll m=(l+r)/2;
if(chk(m)) r=m-1;
else l=m+1;
}
return l;
}
int main(){
scanf("%d%d%d",&n,&c,&t);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
ll ans=half(1,1e10);
printf("%lld",ans);
return 0;
}