A. I Count Two Three
题目大意
在平面上有一个实心圆,圆心为
(Ox,Oy)
,半径为
r
,圆外有一个大小可忽略的小球
思路 - 计算几何
模版题,很容易就能想到判断方法:先判断
B
是否在原始的直线上:
①若在,再判断线段
②若不在,设反射点为
C
,此时如果线段
但是写的时候感觉反射直线不好求,队友提醒可以求出反射点
C
,再求出
代码
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define zero(x) (((x) > 0 ? (x) : -(x)) < EPS)
using namespace std;
const double EPS = 1e-8;
struct Point {
double x, y;
Point() {};
Point(double xx, double yy) :x(xx), y(yy) {}
Point operator + (const Point& a) const {
return Point(x + a.x, y + a.y);
}
};
inline double xmult(const Point& p1,const Point& p2,const Point& p0) {
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
inline double xmult(double x1,double y1,double x2,double y2,double x0,double y0) {
return (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
}
inline bool dots_inline(const Point& p1,const Point& p2,const Point& p3) {
return zero(xmult(p1,p2,p3));
}
inline double distance(const Point& p1, const Point& p2) {
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
inline double distance(double x1,double y1,double x2,double y2) {
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
Point intersection(const Point& u1,const Point& u2,const Point& v1,const Point& v2) {
Point ret=u1;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
/((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
ret.x+=(u2.x-u1.x)*t;
ret.y+=(u2.y-u1.y)*t;
return ret;
}
//计算直线与圆的交点,保证直线与圆有交点
//计算线段与圆的交点可用这个函数后判点是否在线段上
void intersection_line_circle(const Point& c,double r,const Point& l1,const Point& l2,Point& p1,Point& p2) {
Point p=c;
double t;
p.x+=l1.y-l2.y;
p.y+=l2.x-l1.x;
p=intersection(p,c,l1,l2);
t=sqrt(r*r-distance(p,c)*distance(p,c))/distance(l1,l2);
p1.x=p.x+(l2.x-l1.x)*t;
p1.y=p.y+(l2.y-l1.y)*t;
p2.x=p.x-(l2.x-l1.x)*t;
p2.y=p.y-(l2.y-l1.y)*t;
}
Point ptoline(const Point& p,const Point& l1,const Point& l2) {
Point t=p;
t.x+=l1.y-l2.y,t.y+=l2.x-l1.x;
return intersection(p,t,l1,l2);
}
double disptoline(const Point& p, const Point& l1, const Point& l2) {
return distance(p, ptoline(p, l1, l2));
}
bool intersect_seg_circle(const Point& c,double r, const Point& l1,const Point& l2) {
double t1=distance(c,l1)-r,t2=distance(c,l2)-r;
Point t=c;
if (t1<-EPS||t2<-EPS)
return t1>-EPS||t2>-EPS;
t.x+=l1.y-l2.y;
t.y+=l2.x-l1.x;
return xmult(l1,c,t)*xmult(l2,c,t)<EPS&&disptoline(c,l1,l2)-r<-EPS;
}
Point cir, a, b, v;
Point p[4], tmp;
double r;
double judge() {
if(dots_inline(b, a, a + v)) {
return !intersect_seg_circle(cir, r, a, b);
}
intersection_line_circle(cir, r, a, a + v, p[0], p[1]);
if(distance(a, p[1]) < distance(a, p[0])) {
swap(p[0], p[1]);
}
tmp = ptoline(cir, a, b);
intersection_line_circle(cir, r, cir, tmp, p[2], p[3]);
if(distance(a, p[3]) < distance(a, p[2])) {
swap(p[2], p[3]);
}
return zero(distance(p[0], p[2]));
}
int main() {
int T, kase = 0;
scanf("%d", &T);
while(kase < T) {
scanf("%lf%lf%lf", &cir.x, &cir.y, &r);
scanf("%lf%lf%lf%lf", &a.x, &a.y, &v.x, &v.y);
scanf("%lf%lf", &b.x, &b.y);
printf("Case #%d: %s\n", ++kase, judge() ? "Yes" : "No");
}
return 0;
}
B. Binary Tree
题目大意
有一个满二叉树,根结点数为
思路 - 构造
数据范围这么大,还极其富有规律,所以想到是利用二进制构造,但是没有注意到 n<=2k ,导致一直在用奇怪的方法 WA 。
看了题解后才注意到
n<=2k
这个条件,发现每次只走最左边且均取正,结果为
2k−1
,若最后一次往右走则可取到
2k
,刚好取到
n
的最大值。
所以只走最左边
若
n
为奇数,则最后一步向左走,否则最后一步向右走(原因见下面分析),最后可得到对应的和
代码
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
using namespace std;
const int MAXN = 200000;
int n, cnt, len;
string s;
long double ans[MAXN];
bool judge() {
cnt = 0;
len = s.size();
while(s[cnt] == '0') {//除去前导0
++cnt;
}
if(len - cnt > 6) {
return false;
}
n = 0;
while(cnt < len) {
n = n * 10 +s[cnt++] - '0';
}
return n < MAXN;
}
int main() {
ans[0] = 0;
for(int i = 1; i < MAXN; ++i) {
ans[i] = ans[i - 1] + 1.0 / ((0LL + i) * i);
}
while(cin >> s) {
if(judge()) {
cout<<fixed;
cout.precision(5);
cout << ans[n] << endl;
}
else {
cout<<fixed;
cout.precision(5);
cout << ans[MAXN - 1] << endl;
}
}
}
F. Friendship of Frog
题目大意
有一个字符串,求任意两个相同字母间距离的最小值?
思路 - 枚举
字符串很短,所以直接枚举即可。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int dis, len;
char s[1003];
int main() {
int T, kase = 0;
scanf("%d", &T);
while(kase < T) {
scanf("%s", s);
len = strlen(s);
dis = INF;
for(int i = 0; i < len; ++i) {
for(int j = i +1; j < len; ++j) {
if(s[i] == s[j]) {
dis = min(dis, j - i);
break;
}
}
}
printf("Case #%d: %d\n", ++kase, dis == INF ? -1 : dis);
}
return 0;
}
K. Kingdom of Black and White
题目大意
有一个
思路 - 枚举
可以贪心地只反转每一段的两边的两个字符,然后预处理出每一段的长度,先算出初始的值,然后可以枚举反转的字符(注意当前段只有一个字符时,需要将三段合起来计算), O(1) 内即可算的反转后的值,总时间复杂度为 O(n) 。
我用的预处理出每一段的起始和终止的下标,然后枚举反转每一个字符,分类讨论,比较麻烦。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int len;
int sta[100003], des[100003];
long long ans, ori;
char s[100003];
inline long long square(long long a) {
return a * a;
}
int main() {
int T, kase = 0;
scanf("%d", &T);
while(kase < T) {
scanf("%s", s + 1);
len = strlen(s + 1);
sta[1] = 1;
des[len] = len;
for(int i = 2, j = len - 1; i <= len && j > 0; ++i, --j) {
sta[i] = (s[i] == s[i - 1] ? sta[i - 1] : i);
des[j] = (s[j] == s[j + 1] ? des[j + 1] : j);
}
sta[0] = 1;
des[0] = 0;
sta[len + 1] = len + 1;
des[len + 1] = len;
ori = 0;
for(int i = 2; i <= len + 1; ++i) {
if(s[i] != s[i - 1]) {
ori += square(des[i - 1] - sta[i - 1] + 1);
}
}
ans = ori;
s[0] = '\0';
for(int i = 1; i <= len; ++i) {
if(s[i] == s[i - 1]) {
if(s[i] == s[i + 1]) {
ans = max(ans, ori - square(des[i] - sta[i] + 1LL)
+ square(i - sta[i - 1]) + 1LL + square(des[i] - i));
}
else {
ans = max(ans, ori - square(des[i] - sta[i] + 1LL)
- square(des[i + 1] - i)
+ square(i - sta[i - 1]) + square(des[i + 1] - i + 1LL));
}
}
else {
if(s[i] == s[i + 1]) {
ans = max(ans, ori - square(i - sta[i - 1])
- square(des[i] - sta[i] + 1LL)
+ square(i - sta[i - 1] + 1LL) + square(des[i] - i));
}
else {
ans = max(ans, ori - square(des[i - 1] - sta[i - 1] + 1LL)
- 1LL - square(des[i + 1] - i)
+ square(des[i + 1] - sta[i - 1] + 1LL));
}
}
}
printf("Case #%d: %I64d\n", ++kase, ans);
}
return 0;
}