• 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
    ", (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,那么肯定不反射,直接利用勾股定理求时长即可

    最后是反射的情况:

    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
    ", 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;
    }
    
  • 相关阅读:
    Silverlight 4 新特性之NotificationWindow
    如何理解JavaScript原型
    惹恼程序员的十件事
    浅谈HTTP中Get与Post的区别
    asp中Access与Sql Server数据库区别总结
    SQL208语句
    jQuery源码分析
    3. 在 as 和 强制类型转换之间,优先使用 as 操作符。
    揭秘10项必学的.NET技术
    如何设置远程访问SQL Server2005
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14172177.html
Copyright © 2020-2023  润新知