A Alice's Print Service
大意:
打印店打印不同的页数收费不同,现在给出不同的n个区间收费标准,以及m次询问,问最省钱的花费是多少
m和n数据范围为1e5
思路:
先从后往前扫一遍n个区间,预处理出来对于每个区间来说,多打印到哪个区间最省钱,然后二分去找每个询问属于哪个区间,判断是直接打印还是凑整
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int t, n, m;
int p[N], vul[N], les[N];
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d%d", &p[i], &vul[i]);
}
LL maxn = 0x3f3f3f3f3f3f3f3f;
int pos = n;
for (int i = n - 1; i > 0; i--) {
if ((LL)p[i] * vul[i] < maxn) {
pos = i;
maxn = (LL)p[i] * vul[i];
}
les[i - 1] = pos;
}
for (int i = 0; i < m; i++) {
int page;
scanf("%d", &page);
pos = upper_bound(p, p + n, page) - p;
if (pos == n)
printf("%lld
", (LL)page * vul[n - 1]);
else
printf("%lld
", min((LL)page * vul[pos - 1],
(LL)vul[les[pos - 1]] * p[les[pos - 1]]));
}
}
return 0;
}
B Bob's new toy
大意:
思路:
C Collision
大意:
给出一个半径为R的大圆,这个圆里面有一个同心圆model,现在向这个model发射一枚硬币(给出速度vx,vy),碰到model会反射,问硬币在大圆内运行的时间(保证硬币的初始位置在圆外)
思路:
首先判断是否是向大圆方向射去,也就是点到圆心的向量和速度向量的夹角是否小于90度,不小于90度则肯定不会相交,判断夹角小于90度可以用点积大于0来求。
然后判断运行轨迹是否能碰到model,这里用点到直线距离来求即可,如果原点到直线的距离小于等于r+Rm,那么肯定不反射,直接利用勾股定理求时长即可
最后是反射的情况:
利用两次勾股定理算出x,因为是对称的,直接乘2即可
#include <bits/stdc++.h>
using namespace std;
// 计算几何模板
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
// 和0做比较
int sgn(double x) {
if (fabs(x) < eps) return 0; // =0
if (x < 0)
return -1; // < 0
else
return 1; // > 0
}
// 计算x的平方
inline double sqr(double x) { return x * x; }
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x;
y = _y;
}
void input() { scanf("%lf%lf", &x, &y); }
void output() { printf("%.2f %.2f
", x, y); }
bool operator==(Point b) const {
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator<(Point b) const {
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
//叉积
double operator^(const Point &b) const { return x * b.y - y * b.x; }
//点积
double operator*(const Point &b) const { return x * b.x + y * b.y; }
//返回向量长度
double len() {
// hypot(x, y), 即sqrt(x * x + y * y)
return hypot(x, y); //库函数
}
//返回长度的平方
double len2() { return x * x + y * y; }
//返回两点的距离
double dist(Point p) { return hypot(x - p.x, y - p.y); }
Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }
Point operator*(const double &k) const { return Point(x * k, y * k); }
Point operator/(const double &k) const { return Point(x / k, y / k); }
};
struct Line {
Point s, e;
Line() {}
// 两点确定一条线段
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
// 求线段长度
double length() { return s.dist(e); }
//返回直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
//点到直线的距离
double dispointtoline(Point p) {
return fabs((p - s) ^ (e - s)) / length();
}
};
double Rm, R, r, x, y, vx, vy;
int main() {
while (scanf("%lf%lf%lf%lf%lf%lf%lf", &Rm, &R, &r, &x, &y, &vx, &vy)!=EOF) {
if (Point(-x, -y) * Point(vx, vy) <= 0)
printf("0.0000
");
else {
Line l = Line(Point(x, y), Point(x + vx, y + vy));
double d = l.dispointtoline(Point(0, 0));
if (d >= (R + r))
printf("0.0000
");
else {
if (d >= (Rm + r)) {
double t = 2 * sqrt((r+R) * (r+R) - d * d) / hypot(vx, vy);
printf("%.4lf
", t);
} else {
double t = 2 *
(sqrt((r+R) * (r+R) - d * d) -
sqrt((r + Rm) * (r + Rm) - d * d)) /
hypot(vx, vy);
printf("%.4lf
", t);
}
}
}
}
}
D Arnold
大意:
思路:
E Easy Problem Once More
大意:
思路:
F Winter's Coming
大意:
思路:
G Graph Reconstruction
大意:
给出n个点的度,问能否根据这些点还原出一个无向图,如果有多个可行解,输出两个即可
思路:
利用Havel-Hakimi算法,不停地将点按度数排序,然后将度数最大的点和后面的点连线即可。
至于判断多解,只需要判断连线的时候有没有相同的度数的点没有被连线,证明这两个点可以互换
另外这个题在hdu过不了...zoj可以过,应该是没有special judge的问题
#include <bits/stdc++.h>
using namespace std;
struct node {
int id, v;
node() {}
bool operator<(const node &c) const { return v > c.v; }
} h[110];
int n, u[110 * 110], v[110 * 110], m, s, t, r;
int Havel_Hakim() { //-1不可图 0唯一图 1多图
int i, j, flag = 0;
for (i = 0; i < n; i++) { // 每次删除一个元素,最后做n次操作
sort(h + i, h + n); // 每次按照从大到小排序
if (h[i].v == 0) return flag; // 一旦出现度为0,直接返回
if (i + h[i].v >= n)
return -1; // 要删除h[i].v个元素,如果无法删除,那么说明不可图
for (j = i + 1; j <= i + h[i].v; j++) { // 从i+1开始,删除h[i].v个元素
h[j].v--;
if (h[j].v < 0) return -1; // 删除不了,说明不可图
u[m] = h[i].id; // 记录一下连边情况: u[i]--v[i]
v[m++] = h[j].id;
}
if (j < n && h[j].v == h[j - 1].v + 1) {
// 之前删除的最后一个点和后一个点的度为+1关系:表明没删除前的度相同,则可以把这两个点互换
flag = 1; // 表明可以多图
s = m - 1; // s记录最后一条边的下标
t = h[j].id; // 记录一下可交换的元素
r = h[j - 1].id;
}
}
}
// 打印整张图
void put() {
printf("%d %d
", n, m); // 打印节点数和边数
if (m == 0)
printf("
");
else { // u[i] -- v[i]表示一条边
for (int i = 0; i < m; i++)
printf("%d%c", u[i], i == m - 1 ? '
' : ' ');
for (int i = 0; i < m; i++)
printf("%d%c", v[i], i == m - 1 ? '
' : ' ');
}
}
int main() {
while (scanf("%d", &n) != EOF) {
m = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &h[i].v); // 记录度
h[i].id = i + 1; // 记录下标
}
int H = Havel_Hakim(); // 判断是否可以多图
if (H == -1)
printf("IMPOSSIBLE
");
else if (H == 0) { // 打印唯一图
printf("UNIQUE
");
put();
} else { // 打印多图中的2个图
printf("MULTIPLE
");
put();
v[s] = t;
for (int i = s + 1; i < m; i++) { // 遇到r直接换为t,遇到t换为r
if (u[i] == t)
u[i] = r;
else if (u[i] == r)
u[i] = t;
if (v[i] == t)
v[i] = r;
else if (v[i] == r)
v[i] = t;
}
put();
}
}
return 0;
}
H Skycity
大意:
给出圆台的最上层的圆的半径r和最下层的R,总层数F,总高度H,最小玻璃面积S,玻璃为矩形,要求将每一层的圆柱围起来,形成一个外接正多边形,要求出这样围所用的最小玻璃总面积
思路:
从上往下计算每一层所需要的外接多边形的边数,然后计算即可(因为题目说是每一层的天花板上添加玻璃,所以只能从上往下计算每一层的半径,不能从下往上)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
double const PI=acos(-1);
double r, R, h, f, s;
int main() {
while (cin >> R >> r >> h >> f >> s) {
double res = 0;
h = h / f;
for (int i = 0; i < f; i++) {
double rnow = r + (R - r) / f * i;
int n = PI / atan(s / h / 2 / rnow);
res += tan(PI / n) * rnow * 2.0 * h * n;
}
printf("%.3lf
", res);
}
return 0;
}
I LIKE vs CANDLE
大意:
like和candle两个人pk,有n个观众通过转发微博表达自己的支持,每个观众都有一个价值,加到其支持的人那一边,现在Edward可以从like的一边拿出X 的价值去翻转一个账户,即把它的态度换到相反的一边。但是Edward 发现,有的账户已经被别人翻转过了,对于这些账户,Edward就要花费Y的价值去翻转它们。一旦一个账户被翻转了一次,它的所有子账户也会被翻转一次。求like的一边的价值总数与candle一边的价值总数的最大差值。若最大差值为负数则输出“HAHAHAOMG”。
思路:
树形dp,(dp[i][0])代表i没有被翻转的最大值,(dp[i][1])代表已经被翻转了的。因为输入的是初始的状态,所以需要每次将翻转的状态传到子节点,然后在父节点计算dp数组
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
typedef long long LL;
int n, x, y;
vector<int> mp[N];
int v[N], s[N], p[N], dp[N][2];
void dfs(int now, int state) {
state = state ^ s[now];
if (state == 1) {
v[now] = -v[now];
}
dp[now][0] = v[now];
dp[now][1] = -v[now];
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i];
dfs(ne, state);
if (s[ne]) {
dp[now][0] += max(dp[ne][0], dp[ne][1] - y);
dp[now][1] += max(dp[ne][0] - y, dp[ne][1]);
} else {
dp[now][0] += max(dp[ne][0], dp[ne][1] - x);
dp[now][1] += max(dp[ne][0] - x, dp[ne][1]);
}
}
}
int main() {
while (scanf("%d%d%d", &n, &x, &y) != EOF) {
for (int i = 0; i <= n; i++) {
mp[i].clear();
}
for (int i = 1; i <= n; i++) {
int f;
scanf("%d%d%d%d", &v[i], &f, &s[i], &p[i]);
mp[f].push_back(i);
if (p[i] == 1) v[i] = -v[i];
// dp[i][0] = dp[i][1] = 0;
}
dfs(0, 0);
if (dp[0][0] < 0) {
printf("HAHAHAOMG
");
} else {
printf("%d
", dp[0][0]);
}
}
return 0;
}
J Josephina and RPG
大意:
给出一个数n,然后是一个(C_{n}^{3}*C_{n}^{3})的矩阵,代表编号为i的团队种类击败编号为j的团队种类的几率,然后给出一个数m,接下来是m个数,代表需要依次击败的敌人的团队种类,每次击败一个敌人,可以选择换成这个敌人的团队种类,问击败所有敌人的几率是多少
思路:
概率dp,(dp[i][j])代表当前是j可以击败第i个到第n个敌人的概率,从后往前考虑,对于每个种类j,比较利用这个团队击败当前敌人后换成这个敌人的团队还是不换:
for (int j = 0; j < num; j++) {
dp[i][j] = p[j][team[i]] * max(dp[i + 1][j], dp[i + 1][team[i]]);
}
最后输出最大的(dp[0][j])即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long LL;
int n, m, team[N];
double p[150][150], dp[N][150];
int main() {
while (scanf("%d",&n)!=EOF) {
int num = n * (n - 1) * (n - 2) / 6;
for (int i = 0; i < num; i++) {
for (int j = 0; j < num; j++) {
scanf("%lf", &p[i][j]);
}
}
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d", &team[i]);
for (int j = 0; j < num; j++) {
dp[i][j] = 0;
}
}
for (int i = 0; i < num; i++) {
dp[m][i] = 1.0;
}
for (int i = m - 1; i >= 0; i--) {
for (int j = 0; j < num; j++) {
dp[i][j] =
p[j][team[i]] * max(dp[i + 1][j], dp[i + 1][team[i]]);
}
}
double res = 0.0;
for (int i = 0; i < num; i++) {
res = max(res, dp[0][i]);
}
printf("%.7lf
", res);
}
return 0;
}
K Pocket Cube
大意:
给出一个(2*2*2)的魔方的初始状态,问最多n((n<=7))次旋转,可以完成最多多少面
思路:
n很小,直接暴力搜索即可,至于怎么进行模拟旋转,苦思无果,看题解发现直接暴力存在数组里....
注意因为是(2*2*2)的,所以这一面旋转也就相当于正对的那一面旋转了,所以只需要考虑3个面的顺时针和逆时针,6种旋转情况
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int color[30], n;
map<string, int> mp;
queue<string> q;
queue<int> step;
int res;
int turn[6][24] = {
//六种旋转情况
{0, 1, 8, 14, 4, 3, 7, 13, 17, 9, 10, 2,
6, 12, 16, 15, 5, 11, 18, 19, 20, 21, 22, 23},
{0, 1, 11, 5, 4, 16, 12, 6, 2, 9, 10, 17,
13, 7, 3, 15, 14, 8, 18, 19, 20, 21, 22, 23},
{1, 3, 0, 2, 23, 22, 4, 5, 6, 7, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 9, 8},
{2, 0, 3, 1, 6, 7, 8, 9, 23, 22, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 5, 4},
{6, 1, 12, 3, 5, 11, 16, 7, 8, 9, 4, 10,
18, 13, 14, 15, 20, 17, 22, 19, 0, 21, 2, 23},
{20, 1, 22, 3, 10, 4, 0, 7, 8, 9, 11, 5,
2, 13, 14, 15, 6, 17, 12, 19, 16, 21, 18, 23},
};
void bfs() {
while (!q.empty()) {
string now = q.front();
q.pop();
int nowstep = step.front();
step.pop();
int num = 0;
if (now[0] == now[1] && now[1] == now[2] && now[2] == now[3]) num++;
if (now[6] == now[7] && now[7] == now[12] && now[12] == now[13]) num++;
if (now[8] == now[9] && now[9] == now[14] && now[14] == now[15]) num++;
if (now[4] == now[5] && now[5] == now[10] && now[10] == now[11]) num++;
if (now[16] == now[17] && now[17] == now[18] && now[18] == now[19])
num++;
if (now[20] == now[21] && now[21] == now[22] && now[22] == now[23])
num++;
res = max(res, num);
if (nowstep == n) continue;
for (int i = 0; i < 6; i++) {
string New = now;
for (int j = 0; j < 24; j++) {
New[j] = now[turn[i][j]];
}
if (mp[New] == 0) {
mp[New] = 1;
q.push(New);
step.push(nowstep + 1);
}
}
}
}
int main() {
while (cin >> n) {
string s = "";
res = 0;
mp.clear();
while (!q.empty()) {
q.pop();
step.pop();
}
for (int i = 0; i < 24; i++) {
cin >> color[i];
s.push_back(color[i] + '0');
}
mp[s] = 1;
q.push(s);
step.push(0);
bfs();
cout << res << endl;
}
return 0;
}