A. 蠕虫爬井
题意:
初始在0,爬n高度的井,爬1分钟u距离,休息1分钟掉d距离,问几分钟出井?
分析:
如果d>=u,每次做无用功肯定爬不出去,不然肯定可以出去
最后一次可以直接出去,所以先减去,然后看剩下的要多久,注意特判一开始直接出去的情况
代码:
//
// Created by TaoSama on 2015-11-18
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, u, d;
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d%d", &n, &u, &d) == 3) {
if(u >= n) puts("1");
else if(u <= d) puts("The worm can't escape from the well.");
else {
int ans = 1;
n -= u; u -= d;
ans += (n + u - 1) / u * 2;
printf("%d\n", ans);
}
}
return 0;
}
B. 回文数挑选
题意:
判断一个18位数字是否是回文数
分析:
签到题
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
char s[25];
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%s", s) == 1) {
int n = strlen(s);
bool ok = true;
for(int i = 0; i < n >> 1; ++i) {
if(s[i] != s[n - i - 1]) {
ok = false;
break;
}
}
printf("%s ", s);
puts(ok ? "is a palindromic number." : "is not a palindromic number.");
}
return 0;
}
C. 有趣的数字图形
题意:
蛇形填数
分析:
古老的模拟题了,注意边界什么的就好
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, a[15][15];
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int kase = 0;
while(scanf("%d", &n) == 1) {
memset(a, 0, sizeof a);
int x, y, cnt = a[x = 1][y = 1] = 1;
while(cnt < n * n) {
while(y + 1 <= n && !a[x][y + 1]) a[x][++y] = ++cnt;
while(x + 1 <= n && !a[x + 1][y]) a[++x][y] = ++cnt;
while(y - 1 >= 1 && !a[x][y - 1]) a[x][--y] = ++cnt;
while(x - 1 >= 1 && !a[x - 1][y]) a[--x][y] = ++cnt;
}
if(kase++) puts("");
for(x = 1; x <= n; ++x) {
for(y = 1; y <= n; ++y)
printf("%3d", a[x][y]);
puts("");
}
}
return 0;
}
D. 奇怪的木匠
题意:
求一个长宽高为[0,10],小数位数最多为5位浮点数的立方体,切成小正方体,求最少能切多少个,不能有剩余
分析:
求个gcd就是最小的gcd,浮点有误差读字符串转化一下,注意会炸int
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
typedef long long LL;
LL n, a, b, c, OFF = 1e5;
string l, w, h;
LL ten(int x) {
LL ret = 1;
for(int i = 1; i <= x; ++i) ret *= 10;
return ret;
}
void handle(string &l, LL& a) {
if(l.find('.') == string::npos) {
a = atoi(l.c_str()) * OFF;
return;
}
int p = l.find('.'); l.erase(p, 1);
a = atoi(l.c_str()) * ten(5 - (l.size() - p));
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; cin >> t;
while(t--) {
cin >> l >> w >> h;
handle(l, a);
handle(w, b);
handle(h, c);
LL each = __gcd(a, __gcd(b, c));
LL ans = a * b * c / (each * each * each);
cout << ans << '\n';
}
return 0;
}
E. 加倍或清0
题意:
两行整数,相等的数可以匹配,求交叉线段的个数,一个数只能匹配被一次,交叉的线段的两段数不能是相同的数
分析:
显然的状态dp[i][j]:=第一行匹配到第i个,第二行匹配到第j个的最大匹配数
由于暴力转移的决策数是O(n2)的,显然本题需要O(1)的转移,观察可以发现,最近的决策一定是最优的
越近的话,之前匹配的越多
考虑预处理在dp[i][j]状态下第二行与a[i]相同的<j的最大位置down[i][j]
同理在在dp[i][j]状态下第一行与b[j]相同的<i的最大位置up[i][j]
然后转移就好了,需要注意的时候每次需要取到之前最优的状态,不然没法取最近的决策来O(1)转移
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m, a[N], b[N];
int dp[N][N], down[N][N], up[N][N];
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", a + i);
for(int i = 1; i <= m; ++i) scanf("%d", b + i);
for(int i = 2; i <= n; ++i) {
for(int j = 2; j <= m; ++j) {
if(a[i] == b[j - 1]) down[i][j] = j - 1;
else down[i][j] = down[i][j - 1];
}
}
for(int i = 2; i <= n; ++i) {
for(int j = 2; j <= m; ++j) {
if(b[j] == a[i - 1]) up[i][j] = i - 1;
else up[i][j] = up[i - 1][j];
}
}
for(int i = 2; i <= n; ++i) {
for(int j = 2; j <= m; ++j) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
if(a[i] == b[j]) continue;
int a = down[i][j], b = up[i][j];
if(a && b) dp[i][j] = max(dp[i][j], dp[b - 1][a - 1] + 2);
}
}
printf("%d\n", dp[n][m]);
}
return 0;
}
F. 三角形中的格点
题意:
求非退化三角形内部的格点数
分析:
考虑pick定理:顶点在格点上的多边形面积公式:S=a+b÷2−1,其中a表示多边形内部的点数,b表示多边形边界上的点数,s表示多边形的面积
我们需要求a,由于有除2我们全部乘2得到:2a=2S+2−b
三角形面积可以用向量叉积来做,b的话三角形边上的点(不考虑端点),可以用gcd(x,y)−1来求
问题解决
坑:
注意abs啊,gcd和求三角形面积的时候都需要注意
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
struct Point {
int x, y;
void read() {scanf("%d%d", &x, &y);}
Point operator- (const Point& p) const {
return (Point) {x - p.x, y - p.y};
}
int operator^(const Point& p) const {
return x * p.y - y * p.x;
}
} a[3];
int getArea2() {
return (a[1] - a[0]) ^ (a[2] - a[0]);
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(true) {
bool ok = false;
for(int i = 0; i < 3; ++i) {
a[i].read();
if(a[i].x || a[i].y) ok = true;
}
if(!ok) break;
//pick theorem S = in + side/2 - 1
int s = abs(getArea2()), side = 3;
for(int i = 0; i < 3; ++i) {
Point t = a[i] - a[(i + 1) % 3];
side += abs(__gcd(t.x, t.y)) - 1;
}
int ans = s + 2 - side >> 1;
printf("%d\n", ans);
}
return 0;
}
G. Cross a Lake
题意:
N≤105的序列,选出有≥2个元素的子序列,且相邻元素差值不超过H的子序列个数,答案模9901
分析:
显然的dp,dp[i]:=以a[i]结尾的满足要求的子序列个数
dp[i]=dp[j]+⋯+dp[k]+1,dp[j]为以a[i]−h的结尾的,dp[k]为以a[i]+h的结尾的满足要求的子序列个数
多1个是只有自己的情况,显然转移是O(n)的不行,由于是个区间和,我们可以考虑用BIT来维护dp[i]的前缀和,就可以在O(logn)转移了
数字太大,我们需要离散化
由于只有1个的情况是不行的题目要求≥2个元素的子序列,最终答案还需要减去n个1个元素的子序列
注意:
由于有负数,而且模数太小,所以需要ans=((x−y)%MOD+MOD)%MOD
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 9901;
int n, h, a[N], b[N];
void add(int i, int v) {
for(; i <= n; i += i & -i) b[i] += v;
}
int sum(int i) {
int ret = 0;
for(; i; i -= i & -i) ret += b[i];
return ret;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d", &n, &h) == 2) {
vector<int> xs;
for(int i = 1; i <= n; ++i) {
scanf("%d", a + i);
xs.push_back(a[i]);
}
sort(xs.begin(), xs.end());
xs.resize(unique(xs.begin(), xs.end()) - xs.begin());
memset(b, 0, sizeof b);
for(int i = 1; i <= n; ++i) {
int o = lower_bound(xs.begin(), xs.end(), a[i]) - xs.begin() + 1;
int l = lower_bound(xs.begin(), xs.end(), a[i] - h) - xs.begin() + 1;
int r = upper_bound(xs.begin(), xs.end(), a[i] + h) - xs.begin();
int tmp = (sum(r) - sum(l - 1)) % MOD;
add(o, tmp + 1);
}
int ans = ((sum(n) - n) % MOD + MOD) % MOD;
printf("%d\n", ans);
}
return 0;
}
H. A Pilot in Danger
题意:
求点(0,0)是否在n≤16个点的任意多边形内,在就输出px+qy不能构成数的个数(x,y≥0, 2≤p,q≥1000的素数),不能就输出safe
分析:
点在多边形内直接使用转角法的模版判断
第二问ans=(p−1)(q−1)/2 证明见: 戳我看证明
赛后数据加强了,赛上大胆猜想≥p∗q一定可以被构造,可以使用exgcd求出一组解
然后暴力p∗q范围内有多少个不可能构成,也可以藉此来找出规律,找到结论
代码:
//
// Created by TaoSama on 2015-11-19
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const double EPS = 1e-8;
int n;
int sgn(double x) {return x < -EPS ? -1 : x < EPS ? 0 : 1;}
struct Point {
double x, y, ang;
Point(double x = 0, double y = 0): x(x), y(y) {}
void read() {scanf("%lf%lf", &x, &y); ang = atan2(y, x);}
Point operator- (const Point& p) const {
return (Point) {x - p.x, y - p.y};
}
double operator* (const Point& p) const {
return x * p.x + y * p.y;
}
double operator^ (const Point& p) const {
return x * p.y - y * p.x;
}
bool operator< (const Point& p) const {
return ang < p.ang;
}
} a[20];
bool onSeg(Point p, Point a, Point b) {
return sgn((a - p ^ b - p) == 0 && sgn((a - p) * (b - p)) <= 0);
}
int isPointInPolygon(Point p) {
int wn = 0;
for(int i = 0; i < n; ++i) {
if(onSeg(p, a[i], a[i + 1])) return -1;
int k = sgn(a[i + 1] - a[i] ^ p - a[i]);
int d1 = sgn(a[i].y - p.y);
int d2 = sgn(a[i + 1].y - p.y);
if(k > 0 && d1 <= 0 && d2 > 0) ++wn;
if(k < 0 && d2 <= 0 && d1 > 0) --wn;
}
if(wn != 0) return 1;
return 0;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int kase = 0;
while(scanf("%d", &n) == 1 && n) {
for(int i = 0; i < n; ++i) a[i].read();
int p, q; scanf("%d%d", &p, &q);
a[n] = a[0];
printf("Pilot %d\n", ++kase);
if(isPointInPolygon(Point(0, 0)) == 0) {
puts("The pilot is safe.\n");
continue;
}
puts("The pilot is in danger!");
printf("The secret number is %d.\n\n", (p - 1) * (q - 1) >> 1);
}
return 0;
}