oneman233

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;
}

posted on 2019-10-29 15:32  oneman233  阅读(605)  评论(0编辑  收藏  举报

导航