Loading

ICPC 2013 Changsha Regional

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\n", (LL)page * vul[n - 1]);
            else
                printf("%lld\n", 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,那么肯定不反射,直接利用勾股定理求时长即可

最后是反射的情况:

rt77FS.png

利用两次勾股定理算出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\n", 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\n");
        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\n");
            else {
                if (d >= (Rm + r)) {
                    double t = 2 * sqrt((r+R) * (r+R) - d * d) / hypot(vx, vy);
                    printf("%.4lf\n", t);
                } else {
                    double t = 2 *
                               (sqrt((r+R) * (r+R) - d * d) -
                                sqrt((r + Rm) * (r + Rm) - d * d)) /
                               hypot(vx, vy);
                    printf("%.4lf\n", 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", n, m);  // 打印节点数和边数
    if (m == 0)
        printf("\n\n");
    else {  // u[i] -- v[i]表示一条边
        for (int i = 0; i < m; i++)
            printf("%d%c", u[i], i == m - 1 ? '\n' : ' ');
        for (int i = 0; i < m; i++)
            printf("%d%c", v[i], i == m - 1 ? '\n' : ' ');
    }
}

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\n");
        else if (H == 0) {  // 打印唯一图
            printf("UNIQUE\n");
            put();
        } else {  // 打印多图中的2个图
            printf("MULTIPLE\n");
            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\n", 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\n");
        } else {
            printf("%d\n", 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\n", 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;
}
posted @ 2020-12-22 11:49  WinterFa1L  阅读(146)  评论(0)    收藏  举报