icpc 2017 沈阳个人题解

虽然参加了青岛icpc但那场的题超出我太多..唯一赛后弄会的polya计数已经写到上一篇了...

但沈阳的题目还好比较常规 中等题尤其多

 

I Little Boxes

签到题目 a+b+c+d 小坑点是小于等于2^62 就是说答案小于等于2^64

特判一下和为2^64情况用unsigned long long就好(或者java)

各水平的队伍都有不是1A的..所以还是认真点..20罚时兴许就换一块奖牌

#include

#include

int main()

{

    unsigned long long a, b, c, d;

    int t;

    scanf("%d", &t);

    while (~scanf("%llu%llu%llu%llu", &a, &b, &c, &d))

        if (a == b&&b == c&&c == d&&d == 4611686018427387904)

            printf("%s\n", "18446744073709551616");

        else

            printf("%llu\n", a + b + c + d);

}

 

K Rabbits

签到题 一群兔子在数轴上 每只兔子每次可以向内跳过1只兔子 问最多跳几次

数轴上所有兔子之间的空隙减去2边最小的空隙就是最多跳的次数...

#include

#include

#include

using namespace std;

int a[505], n, t;

int main()

{

    int  i, ans;

    scanf("%d", &t);

    while (t--&&scanf("%d", &n))

    {

        ans = 0;

        for (i = 1; i <= n; i++)

            scanf("%d", a + i);

        for (i = 2; i <= n; i++)

            ans += (a[i] - a[i - 1] - 1);

        ans -= min(a[n] - a[n - 1] - 1, a[2] - a[1] - 1);

        printf("%d\n", ans);

    }

}

 

第三题开始就不太水了..

 

L Tree

题意有点麻烦...给一个树  n种染色 染节点(每个节点唯一染色) 每种染色取出这种颜色的点的最小生成树中的边 求这n个边集的交的集合最多有几个边

有点头大的感觉..

正解是如果1条边的2侧的节点数都大于等于n 则可以把这个边2侧的点染成每侧都有n种颜色 这条边就可以在最终集合里且不影响其他边 ans++

记忆化搜索(dfs)出个节点以下(包括自己)的节点数ch[i] 如果ch[i]>=n且剩下的点数>=n 则ans++

复杂度对节点数是线性的

代码也很好写 难怪有队伍22分钟出了前三题...

#include

#include

#include

using namespace std;

struct { int v, ne; }a[400002];

int k1, h[200002], ch[200002], vi[200002];

void add(int u, int v)

{

    a[k1].v = v, a[k1].ne = h[u], h[u] = k1++;

}

int dfs(int x)

{

    if (ch[x] != 0)

        return ch[x];

    int i, ans = 1;

    vi[x] = 1;

    for (i = h[x]; i; i = a[i].ne)

        if (vi[a[i].v] == 0)

            ans += dfs(a[i].v);

    return ch[x] = ans;

}

int main()

{

    int t, i, n, k, x, y, ans;

    scanf("%d", &t);

    while (t--&&scanf("%d%d", &n, &k))

    {

        memset(h, 0, sizeof(h)), k1 = 1;

        memset(ch, 0, sizeof(ch)), ans = 0;

        memset(vi, 0, sizeof(vi));

        for (i = 1; i < n; i++)

            scanf("%d%d", &x, &y), add(x, y), add(y, x);

        dfs(1);

        for (i = 1; i <= n; i++)

        {

            ch[i] = dfs(i);

            if (ch[i] >= k&&n - ch[i] >= k)

                ans++;

        }

        printf("%d\n", ans);

    }

}

 

F Heron and His Triangle

题意简明 如果三角形三边为t-1,t,t+1且三角形面积是整数 则这个数叫xxx数

求大于等于x的xxx数(10^30)

可以规约到求一个双曲线整点的问题 沉迷于数学分析然而并分析不出来...mathematica分析少了一半的解导致wa..

Acm解法是打表出前几个找规律...答案类似斐波那契的a[i]=4*a[i-1]-a[i-2] 然后java大数一发(c++打表代码写的很难看..)

其实可以规约到一个序列的问题大都是这么做:打表出前几项 通常是斐波那契类似的递推公式 范围不够打表的话可以矩阵快速幂算

#include

#include

#include

#include

using namespace std;

char ans[100][100] = { "4", "14", "52", "194", "724", "2702", "10084", "37634", "140452", "524174", "1956244",

"7300802", "27246964", "101687054", "379501252", "1416317954", "5285770564",

"19726764302", "73621286644", "274758382274", "1025412242452", "3826890587534",

"14282150107684", "53301709843202", "198924689265124", "742397047217294",

"2770663499604052", "10340256951198914", "38590364305191604",

"144021200269567502", "537494436773078404", "2005956546822746114",

"7486331750517906052", "27939370455248878094", "104271150070477606324",

"389145229826661547202", "1452309769236168582484",

"5420093847118012782734", "20228065619235882548452",

"75492168629825517411074", "281740608900066187095844",

"1051470266970439230972302", "3924140458981690736793364",

"14645091568956323716201154", "54656225816843604128011252",

"203979811698418092795843854", "761263020976828767055364164",

"2841072272208896975425612802", "10603026067858759134647087044",

"39571031999226139563162735374", "147681101929045799118003854452",

"551153375716957056908852682434", "2056932400938782428517406875284",

"7676576228038172657160774818702", "28649372511213908200125692399524" }

;

int BigCmp(char * a, char * b//  大数a < 大数b 返回1 ,相等返回0 ,a>b返回-1

{

    int i, j, k;

    int la = strlen(a), lb = strlen(b);

    char * temp;

    if (a[0] == '-' && b[0] != '-')

        return 1;

    else if (a[0] != '-' && b[0] == '-')

        return -1;

    else if (a[0] == '-' && b[0] == '-')

    {

        a++, b++;

        temp = a, a = b, b = temp;

        la--, lb--;

        k = la, la = lb, lb = k;

    }

    if (la < lb)

        return 1;

    if (la > lb)

        return -1;

    if (la == lb)

    {

        for (i = 0; i < la; i++)

        {

            if (a[i] < b[i])

                return 1;

            else if (a[i] > b[i])

                return -1;

        }

    }

    return 0;

}

int main()

{

    int t; scanf("%d", &t);

    while (t--)

    {

        char s[100];

        scanf("%s", s);

        for (int i = 0; i < 55; i++)

            if (BigCmp(s, ans[i])!=-1)

            {

                printf("%s\n", ans[i]);

                break;

            }

    }

}

 

这是前4题,全做出的大部分铜牌剩下的没牌...

 

G Infinite Fraction Path

题意很明确 给一长度为n的字符串 每个点从i有一条通往(i*i)%n的路 求长度为n的通路中的字典序最大的(数字最大)

一个一个字符串的话找遇到最坏情况(所有字符一样)复杂度O(n^2) 考虑bfs按层搜索优化

首先第一个点肯定是最大的字符 让这些点入队 每层最大的数才继续搜下个节点 再去除每层的重复节点(很重要 所有字符一样的时候靠它优化)

算法就是上面一句 实现稍复杂 每次让队列中层数最小的中数字最大的派生下个节点 需要用优先队列 去重节点可以用栈模拟set或直接set(但这里直接用set会tle...)

主要卡的是大多数字符一样是时候的时间 复杂度比较玄学..感觉和(i*i)%n这个式子有关...最后应该是在一个环中的循环 或许环是O(n^0.5)? 总复杂度是O(n^1.5)?

#include

#include

#include

#include

using namespace std;

char a[150005], ans[150005];

int sta[150005], top, la, bo[150005];

struct node {

    int in, step;

    node() {}

    node(int in, int step)

    {

        this->in = in, this->step = step;

    }

};

priority_queue<node>que;

bool operator<(node A, node B)

{

    if (A.step == B.step)

        return a[A.in] < a[B.in];

    return A.step > B.step;

}

int main()

{

    int t, n, i, ca = 1;

    char ma;

    node x;

    scanf("%d", &t);

    while (t--&&scanf("%d%s", &n, a))

    {

        memset(ans, 0, sizeof(ans)), ma = 0, top = 0;

        memset(sta, 0, sizeof(sta)), memset(bo, 0, sizeof(bo));

        for (i = 0; i < n; i++)

            ma = max(ma, a[i]);

        ans[0] = ma, la = 0;

        for (i = 0; i < n; i++)

            if (a[i] == ma)

                que.push(node(i, 0));

        while (!que.empty())

        {

            x = que.top(), que.pop();

            if (x.step >= n)continue;

            if (x.step > la)

            {

                la = x.step;

                while (top)

                    bo[sta[--top]] = 0;

            }

            if (ans[la] > a[x.in] || bo[x.in] == 1)continue;

            sta[top++] = x.in, bo[x.in] = 1;

            ans[la] = a[x.in];

            que.push(node((1LL * x.in*x.in + 1) % n, x.step + 1));

        }

        printf("Case #%d: %s\n", ca++, ans);

    }

    return 0;

}

 

 

M Wandering Robots

面向样例编程系列...

题意很恶心 其实是:每个点的权值是它四周好点的个数+1 坏点权值为0 求总权值和x+y>=n的点的权值的比

翻译好题目就不是太难了 一个需要注意的是如果一个区域全部被围起来 这个区域内所有的点都是坏点(类似hdu5925) 但测试数据中并没有这样的...

数据范围有点大 但坏点不多 先离散化 然后搜索看哪些点是不可达原点的 再统计答案

复杂度大概是4*k^2

但数据很水没有被围起来的点...所以一个一个坏点及其周围的点更新就好..复杂度O(K)

 

 

 

 

 

 

 

 

C Empty Convex Polygons

字面意思 最大空凸包

虽然是金牌题 但红书上有完全一样的模板..所以几个队伍因为出了这题4题得铜 题目模板什么的还是应该多看看

#include

#include

#include

#include

#include

#include

using namespace std;

const int maxn = 100;

const double zero = 1e-8;

struct Vector {

    double x, y;

};

inline Vector operator - (Vector a, Vector b)

{

    Vector c;

    c.x = a.x - b.x;

    c.y = a.y - b.y;

    return c;

}

inline double Sqr(double a) {

    return a*a;

}

inline int Sign(double a) {

    if (fabs(a) <= zero) return 0;

    return a<0 ? -1 : 1;

}

inline bool operator < (Vector a, Vector b)

{

    return Sign(b.y - a.y) >0 || Sign(b.y - a.y) == 0 && Sign(b.x - a.x)>0;

}

inline double Max(double a, double b) {

    return a>b ? a : b;

}

inline double Length(Vector a)

{

    return sqrt(Sqr(a.x) + Sqr(a.y));

}

inline double Cross(Vector a, Vector b) {

    return a.x*b.y - a.y*b.x;

}

Vector dot[maxn], List[maxn];

double opt[maxn][maxn];

int seq[maxn];

int n, len;

double ans;

bool Compare(Vector a, Vector b) {

    int temp = Sign(Cross(a, b));

    if (temp != 0) return temp>0;

    temp = Sign(Length(b) - Length(a));

    return temp>0;

}

void Solve(int vv) {

    int i, j, t, _len;

    for (i = len = 0; i

        if (dot[vv]<dot[i]) List[len++] = dot[i] - dot[vv];

    for (i = 0; i

        for (j = 0; j

            opt[i][j] = 0;

    sort(List, List + len, Compare);

    double v;

    for (t = 1; t

        _len = 0;

        for (i = t - 1; i >= 0 && Sign(Cross(List[t], List[i])) == 0; i--);

        while (i >= 0) {

            v = Cross(List[i], List[t]) / 2;

            seq[_len++] = i;

            for (j = i - 1; j >= 0 && Sign(Cross(List[i] - List[t], List[j] - List[t]))>0; j--);

            if (j >= 0) v += opt[i][j];

            ans = Max(ans, v);

            opt[t][i] = v;

            i = j;

        }

        for (i = _len - 2; i >= 0; i--)

            opt[t][seq[i]] = Max(opt[t][seq[i]], opt[t][seq[i + 1]]);

    }

 

}

int i;

double Empty()

{

    ans = 0;

    for (i = 0; i

        Solve(i);

    return ans;

}

int main()

{

    int t; scanf("%d", &t);

    while (t--)

    {

        scanf("%d", &n);

        for (int i = 0; i < n; i++)

        {

            scanf("%lf%lf", &dot[i].x, &dot[i].y);

        }

        double ans = Empty();

        printf("%.1lf\n", ans);

    }

    return 0;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(acm,个人题解)