2006-2007 ACM-ICPC | POJ3380 POJ3384 POJ3385 水题题解
// CF比赛链接:http://codeforces.com/gym/101650
// POJ链接:http://poj.org/searchproblem?field=source&key=Northeastern+Europe+2006,POJ3379 ~ POJ3389
// Day12 暑训第一阶段最后一场组队赛
// 区域赛难度,表现还可以,前期互相推锅,后半场自闭O.O
B - Bridges
题目大意:
n个地区之间有n-1条道路,两两之间只有一条路径(说明是一个树结构)。初始的道路只有马能走,需要修建k个桥能让车通过,给定马速sh与车速sc,以及地图信息,为了使任意两节点之间所花时间减少最多,求修建k座桥的标号。
分析及代码:
很水的,很容易发现树上每条边走过的次数为两端点子树的总节点个数相乘,要使减少时间最大化,显然应直接选择边长*次数最大的边。
然而测试数据有坑,车速可能比马速还要小,就老老实实算减少时间,排序后选最大的吧。。。
AC代码采取了树上dfs的两种写法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = ;
struct Edge {
int to, id, next;
double w;
}edges[maxn*];
int head[maxn*], tot;
int n, k, sh, sc;
void addEdge(int u, int v, double w, int id) {
edges[tot].to = v;
edges[tot].w = w/sh - w/sc; // 只记录边长会WA,还是按照题意算减少时间吧
edges[tot].id = id;
edges[tot].next = head[u];
head[u] = tot++;
}
struct node {
int id;
double w;
node (int _id, double _w):id(_id), w(_w) {}
bool operator<(const node& a)const {
return w<a.w; // 重载小于号,默认大顶堆
}
};
priority_queue<node> ans;
int cnt;
void dfs(int u, int fa) {
++cnt;
for(int i=head[u];~i;i=edges[i].next) {
int v = edges[i].to;
if(v!=fa) {
int no = cnt;
//cout<<v<<endl;
dfs(v, u);
//edges[i].w *= (ll)(cnt-no)*(n-(cnt-no));
int son = cnt - no; // cnt已更新到叶子节点,两者差为以v节点为根的子树节点总个数
double times = (son+0.0) * (n - son);
ans.push(node(edges[i].id, times*edges[i].w));
//printf("%d->%d:%d\n", u, v, w);
}
}
} int main() { cin>>n>>k>>sh>>sc;
memset(head, -, sizeof(head));
for(int i=;i<n;i++) {
int u, v, l;
scanf("%d %d %d", &u, &v, &l);
addEdge(u, v, l, i);
addEdge(v, u, l, i);
} cnt = ;
dfs(, -); for(int i=;i<=k;i++) {
printf("%d%c", ans.top().id, i==k?'\n':' ');
ans.pop();
}
return ;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = ;
struct Edge {
int to, id, next;
double w;
}edges[maxn*];
int head[maxn*], tot;
int n, k, sh, sc;
void addEdge(int u, int v, double w, int id) {
edges[tot].to = v;
edges[tot].w = w;
edges[tot].id = id;
edges[tot].next = head[u];
head[u] = tot++;
}
struct node {
int id;
double w;
node (int _id, double _w):id(_id), w(_w) {}
bool operator<(const node& a)const {
return w>a.w;
}
};
vector<node> ans;
bool vis[maxn];
int cnt;
void dfs(int u) {
vis[u] = ;
++cnt;
for(int i=head[u];~i;i=edges[i].next) {
int v = edges[i].to;
if(!vis[v]) {
int no = cnt;
//cout<<v<<endl;
dfs(v);
//edges[i].w *= (ll)(cnt-no)*(n-(cnt-no));
int son = cnt - no;
double times = (son+0.0) * (n - son);
ans.push_back(node(edges[i].id, times*edges[i].w));
//printf("%d->%d:%d\n", u, v, w);
}
}
} int main() { cin>>n>>k>>sh>>sc;
memset(head, -, sizeof(head));
for(int i=;i<n;i++) {
int u, v, l;
scanf("%d %d %d", &u, &v, &l);
addEdge(u, v, l, i);
addEdge(v, u, l, i);
} cnt = ;
dfs();
sort(ans.begin(), ans.end());
for(int i=;i<k;i++) {
printf("%d%c", ans[i].id, i==k?'\n':' ');
}
return ;
}
F - Feng Shui
题目大意:
在一块多边形区域铺上两个相同半径的圆形地毯,要使地毯最多与边界相切(不能相交)的情况下,地毯覆盖的面积最大,求两圆心的位置坐标。
分析及代码:
队友分析出,只需要把多边形的每条边往里平移r,得到新的多边形,圆心位置一定在新的多边形端点上,所求圆心为两点之间的距离最长的那两个端点。这样既保证了与边界不相交,求两圆形地毯重叠最少,所以覆盖面积最大。
比赛抄了部分模板,一直WAWA大哭,自闭到比赛结束。
赛后才想到平移之后有的边会消失(移动后的边在多边形外部,如五边形缩小成为了四边形),直接求相邻两边得到新的多边形端点是不对的。

那么该如何修正呢?虽然发现这些边方向会反转,但判断起来太麻烦,因为删去一条边还会影响下一条边。即使算法正确,还需要确定第一条一定相切的边,否则也可能是要删去的边。
其实这题就是半平面交的裸题。
附上计算几何模板+AC代码:(把点乘敲错让我debug了一下午。。。)
#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
using namespace std; // ***********************模板开始****************************
struct Point {
double x, y;
Point (double xx=, double yy=):x(xx), y(yy) {}
};
typedef Point Vector;
typedef vector<Point> Polygon; Vector operator+(Point A, Vector B) {
return Vector(A.x+B.x, A.y+B.y);
}
Vector operator-(Point A, Vector B) {
return Vector(A.x-B.x, A.y-B.y);
}
Vector operator*(Vector A, double t) {
return Vector(A.x*t, A.y*t);
}
// 向量叉乘
double cross(Vector A, Vector B) {
return A.x*B.y - A.y*B.x;
}
// 向量点乘
double dot(Vector A, Vector B) {
return A.x*B.x + A.y*B.y;
}
// 与0比较函数
const double eps = 1e-;
int dcmp(double x) {
if(fabs(x)<eps) return ;
return x<? -:;
}
// p是否在线段AB上
bool onSegment(Point p, Point A, Point B) { // 包含端点
return dcmp(cross(A-p, B-p))== && dcmp(dot(A-p, B-p))<=;
}
// 两直线交点
Point crossPoint(Point P, Vector v, Point Q, Vector w) {
Vector u = P-Q;
double t = cross(w, u)/cross(v, w);
return P+v*t;
}
// 半平面交模板,直线AB切割多边形poly,返回左侧部分
Polygon cutPolygon(Polygon poly, Point A, Point B, int k) {
Polygon newpoly;
int n = poly.size();
for(int i=;i<n;i++) {
Point C = poly[i];
Point D = poly[(i+)%n];
if(dcmp(cross(B-A, C-A))>=) newpoly.push_back(C);
if(dcmp(cross(B-A, C-D))!=) { // C-D与A-B不平行
Point ip = crossPoint(A, B-A, C, D-C);
if(onSegment(ip, C, D)) newpoly.push_back(ip);
}
}
return newpoly;
}
// ***********************模板结束**************************** Point p[]; // AB距离
double dis(Point A, Point B) {
double x = A.x - B.x;
double y = A.y - B.y;
double res = x*x + y*y;
return sqrt(res);
} Polygon init(int n) {
Polygon poly;
for(int i=;i<n;i++) {
poly.push_back(p[i]);
}
return poly;
} int main() {
int n;
double x, y, r;
cin>>n>>r;
for(int i=n-;i>=;i--) {
scanf("%lf %lf", &x, &y);
p[i].x = x;
p[i].y = y;
}
p[n] = p[]; Polygon poly = init(n); for(int i=;i<n;i++) {
double len = dis(p[i], p[i+]);
double dx = p[i+].x - p[i].x;
double dy = p[i+].y - p[i].y; Point A = Point(p[i].x - r*dy/len, p[i].y + r*dx/len);
Point B = Point(p[i+].x - r*dy/len, p[i+].y + r*dx/len); poly = cutPolygon(poly, A, B, i);
} double ans = ;
int p1 = -, p2 = -;
for(int i=;i<poly.size();i++) {
for(int j=i+;j<poly.size();j++) {
if(ans<dis(poly[i], poly[j])) {
p1 = i; p2 =j;
ans = dis(poly[i], poly[j]);
}
}
}
if(p1!=-) {
printf("%.5lf %.5lf %.5lf %.5lf\n", poly[p1].x, poly[p1].y, poly[p2].x, poly[p2].y);
} else { // 缩成一个点,两圆重合
printf("%.5lf %.5lf %.5lf %.5lf\n", poly[].x, poly[].y, poly[].x, poly[].y);
}
return ;
}
G - Genealogy
题目大意:
给你一个家谱图组成的树,要使每个家庭成员的儿子节点不超过d个且不改变祖先-子孙关系,求最少需要加入几个节点。
分析及代码:
翻译成数据结构的语言,本题就是n-叉树的转化问题。
直接记录每个节点儿子的数量,然后计算有超过d个儿子的节点经过变换需要添加的节点个数即可。
AC代码就是不断把d个儿子连上新的节点,并作为新的儿子,直到新的儿子总个数小于等于d(注意不是1啊,让我16组T了!)
四行代码就够了:
int res = 0;
while(k>d) {
res += k/d;
k = k/d + k%d;
}
AC代码:
#include<cstdio>
#include<iostream>
using namespace std; int n, d;
int son[]; int cal(int k) {
int res = ;
while(k>d) {
res += k/d;
k = k/d + k%d;
// cout<<k<<endl;
}
return res; }
int main() {
//while(cin>>n>>d) cout<<cal(n)<<endl;
cin>>n>>d;
for(int i=;i<=n;i++) {
int u;
scanf("%d", &u);
son[u]++;
}
int ans=;
for(int i=;i<=n;i++) {
if(son[i]>d) {
ans += cal(son[i]);
}
}
cout<<ans<<endl;
return ;
}
(未完待续)
2006-2007 ACM-ICPC | POJ3380 POJ3384 POJ3385 水题题解的更多相关文章
- World Finals 2017 (水题题解)
看大佬做2017-WF,我这种菜鸡,只能刷刷水题,勉强维持生活. 赛后补补水题. 题目pdf链接,中文的,tls翻译的,链接在这里 个人喜欢在vjudge上面刷题. E Need for Speed ...
- 2013 ACM/ICPC 长春网络赛E题
题意:给出一个字符串,要从头.尾和中间找出三个完全相等的子串,这些串覆盖的区间互相不能有重叠部分.头.尾的串即为整个字符串的前缀和后缀.问这个相同的子串的最大长度是多少. 分析:利用KMP算法中的ne ...
- 2013 ACM/ICPC 长春网络赛F题
题意:两个人轮流说数字,第一个人可以说区间[1~k]中的一个,之后每次每人都可以说一个比前一个人所说数字大一点的数字,相邻两次数字只差在区间[1~k].谁先>=N,谁输.问最后是第一个人赢还是第 ...
- 2013 ACM/ICPC 长沙网络赛J题
题意:一个数列,给出这个数列中的某些位置的数,给出所有相邻的三个数字的和,数列头和尾处给出相邻两个数字的和.有若干次询问,每次问某一位置的数字的最大值. 分析:设数列为a1-an.首先通过相邻三个数字 ...
- 2013 ACM/ICPC 南京网络赛F题
题意:给出一个4×4的点阵,连接相邻点可以构成一个九宫格,每个小格边长为1.从没有边的点阵开始,两人轮流向点阵中加边,如果加入的边构成了新的边长为1的小正方形,则加边的人得分.构成几个得几分,最终完成 ...
- 2013 ACM/ICPC 长沙现场赛 A题 - Alice's Print Service (ZOJ 3726)
Alice's Print Service Time Limit: 2 Seconds Memory Limit: 65536 KB Alice is providing print ser ...
- 2013 ACM/ICPC 长沙现场赛 C题 - Collision (ZOJ 3728)
Collision Time Limit: 2 Seconds Memory Limit: 65536 KB Special Judge There's a round medal ...
- 2013 ACM/ICPC 杭州网络赛C题
题意:驴和老虎,在一个矩阵的两个格子里,有各自的起始方向.两者以相同的速度向前移动,前方不能走时驴总是向右,老虎总是向左.他们不能超出矩阵边界也不能走自己走过的格子(但可以走对方走过的格子).如果不能 ...
- hdu 4432 第37届ACM/ICPC天津现场赛B题
题目大意就是找出n的约数,然后把约数在m进制下展开,各个数位的每一位平方求和,然后按m进制输出. 模拟即可 #include<cstdio> #include<iostream> ...
随机推荐
- Could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR tensorflow-1.13.1和1.14windows版本目前不支持CUDA10.0
报错出现 Could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR tensorflow-1.13.1和1.14windows版本目前不支持 ...
- 常用的JS方法(见到好的就添加进来)
// 悬浮置顶 ; (function ($) { $.fn.crumbsFixedPosition = function (options) { var defaults = { cName: 'f ...
- 同步图计算实现最短路径Dijkstra算法
同上篇讲述pageRank一样,考虑一个顶点V. 根据顶点算法通常步骤1) 接收上个超步发出的入邻居的消息2) 计算当前顶点的值3) 向出邻居发消息 1.接收入邻居的消息 2.求入邻居的最小值,加上顶 ...
- Es567严格模式
Es5 严格模式 http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html 除了正常运行模式(混杂模式),ES5添加了第二种 ...
- [JZOJ6279] 2019.8.5【NOIP提高组A】优美序列
题目 题目大意 给你一个排列以及若干区间,对于每个区间,问包含它的最小的优美序列的区间. 所谓优美序列,即将权值排序后能够得到连续的排列. 思考历程 优美序列显然满足这个条件:\(mx-mn=r-l\ ...
- Java基本语法.part02
变量 变量的概念: 内存中的一个存储区域 该区域有自己的名称(变量名)和类型(数据类型) Java中每个变量必须先声明,后使用 该区域的数据可以在同一类型范围内不断变化 使用变量注意: 变量的作用域: ...
- csps模拟83最大异或和简单的括号序列旅行计划题解
题面:https://www.cnblogs.com/Juve/articles/11733280.html 最大异或和: 简单博弈,小Q一定不会输,如果异或和为0,则平局,因为无论小Q如何拿,小T都 ...
- python基础-基础知识(包括:函数递归等知识)
老男孩 Python 基础知识练习(三) 1.列举布尔值为 False 的值空,None,0, False, ", [], {}, () 2.写函数:根据范围获取其中 3 和 7 整除的所有 ...
- STM32 解析futaba S-bus协议
S-bus为futaba使用的串行通信协议.实际上为串口通信.但是有几点需要注意: 在大端小端上,网上资料都说的不是很清楚: 跟TTL串口信号相比,S-bus的逻辑电平是反的,需用如下电路对电平反相, ...
- 夏令营501-511NOIP训练17——蛇形矩阵
传送门:QAQQAQ 题意:话说小X在孩提时,都会做标准的蛇形矩阵了,发现很好玩.现在的小X很想对其进行改版,变为如下类型的一个无限大蛇形数阵:令S(x)表示以1为左上角,x为右下角的矩形内所有数之和 ...