牛客寒假算法基础训练营1
C-umi和弓道
链接:https://ac.nowcoder.com/acm/contest/3002/C
来源:牛客网
umi对弓道非常痴迷。
有一天,她在研究一个射箭问题:
在一个无限大的平面中,她站在 (x0,y0) 这个坐标。
有 n个靶子,第i 个靶子的坐标是(xi,yi)
umi准备在 x轴或y 轴上放置一块挡板来挡住弓箭的轨迹,使得她可以射中的靶子数量不超过k 个。
她想知道挡板的最短长度是多少?
注:假定弓箭的轨迹是起点为umi坐标、长度无穷大的射线。umi和靶子的体积可以无视。挡板的边缘碰到弓箭轨迹也可视为挡住弓箭。
注2:挡板不能弯折,起始和终点必须在同一坐标轴上。
一开始看到这个题目跟失了智一样。。。完全没有想法,但其实很简单。每一条射线都会跟X轴或者Y轴存在一个交点,除非与umi同象限,分别将X轴和Y轴上的交点记录下来,然后维护一个长度为K的平板(只在X轴或者Y轴上跨越K个点),不断移动记录最小长度即可。
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
vector<double> x;
vector<double> y;
double tx,ty;
int n,k;
void input(){
scanf("%lf%lf",&tx,&ty);
scanf("%d%d",&n,&k);
k = n - k;
double tempx,tempy;
for(int i = 0;i < n;i++){
scanf("%lf%lf",&tempx,&tempy);
if(tempx * tx < 0){
x.push_back(ty - tx * 1.0 * (ty - tempy) / (tx - tempx));
}
if(tempy * ty < 0){
y.push_back(tx - ty * (tx - tempx) / (ty - tempy));
}
}
}
void work(){
double mi = 1e18;
sort(x.begin(),x.end());
sort(y.begin(),y.end());
if(x.size() >= k){
int head =0,tail = k - 1;
while(tail < x.size()){
mi = min(mi,x[tail] - x[head]);
tail++;
head++;
}
}
if(y.size() >= k){
int head = 0,tail = k - 1;
while(tail < y.size()){
mi = min(mi,y[tail] - y[head]);
tail++;
head++;
}
}
if(mi == 1e18){
printf("-1");
}else{
printf("%.8lf",mi);
}
}
int main(){
input();
work();
return 0;
}
F-maki和tree
链接:https://ac.nowcoder.com/acm/contest/3002/F
来源:牛客网
有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有 n 个顶点,n - 1条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
② <p,q> 和<q,p> 的取法视为同一种。
路径上有且仅有黑色点的一共有两种情况,一种是一个端点是黑色,一个端点是白色,或者两个端点都是白色。那么对于第一种情况,我们只需要知道一个黑色端点下总共连接了多少个白色的点就够了,对于第二种情况,则只要记录下每一个与黑色端点直接相连的白色端点所连接的白色点的个数,俩俩相乘并求和就是最后的答案。我们假设第i个白点的权值(即所连接白色点的个数)为 f ( i ) f(i) f(i),假设一个黑端点连接了K个白点
第一个路径就是 ∑ i = 1 k f ( i ) \sum_{i = 1}^kf(i) ∑i=1kf(i)第二个路径的数量为 ∑ i = 1 k ∑ j = i + 1 k f ( i ) f ( j ) \sum_{i = 1}^k\sum_{j = i + 1}^kf(i)f(j) ∑i=1k∑j=i+1kf(i)f(j)
#include <cstdio>
#include <vector>
using namespace std;
int n;
const int maxn = 1e5 + 7;
vector<int> tree[maxn];
bool flag[maxn] = {
false};
int root[maxn];
int numSon[maxn];
int t[maxn];
int find_root(int n){
if(n == root[n]){
return n;
}
return find_root(root[n]);
}
void uni(int x,int y){
int tempx = find_root(x);
int tempy = find_root(y);
if(tempx != tempy){
if(numSon[tempx] > numSon[tempy]){
root[tempy] = tempx;
numSon[tempx] += numSon[tempy] + 1;
}else{
root[tempx] = tempy;
numSon[tempy] += numSon[tempx] + 1;
}
}
}
void input(){
scanf("%d",&n);
getchar();
for(int i = 1;i <= n;i++){
if(getchar() == 'B'){
flag[i] = true;
}
root[i] = i;
numSon[i] = 0;
}
int a,b;
for(int i = 0;i < n - 1;i++){
scanf("%d%d",&a,&b);
tree[a].push_back(b);
tree[b].push_back(a);
//白色端点并查集合并根,便于后面处理
if(!flag[a] && !flag[b]){
uni(a,b);
}
}
}