2016-2017 ACM-ICPC, Egyptian Collegiate Programming Contest (ECPC 16)【solved:9 / 11】

来源:互联网 发布:双核单片机 编辑:程序博客网 时间:2024/06/05 08:19

差两个题。F题好像是树状数组什么的。不太会。百度能搜到题解。感觉没看懂他在说啥。K题vj上好像有代码。

-update 2017年10月7日15:54:16

A - The game of Osho(SG函数)

题意:

  有m堆石子,每堆有对应一个(Bi,Ni),表示这堆有Ni个石子,每次可以拿Bi^x个石子(1<=Bi^x<=Ni),谁不能拿谁输,1和2轮流拿,两人足够机智,问谁必胜。

思路:

  请看Gym 101147A - The game of Osho(SG函数+二项展开)


B - Street(最短路)

题意:

  有一些矩形的阴影块,问你从一个大矩形地图的底边跑到顶边,要走过的非阴影路程最短是多少。

数据:

  不超过100个阴影块,矩形边长不超过1e9。

思路:

  这题其实很简单,因为它题目说了,既没有矩形相交的情况。块数也只有100,那么是不是只要预处理出俩俩矩形块之间距离,然后跑一个最短路就好了。

  预处理距离的时候,一定要小心,有些逻辑判断先后顺序倒一下,就T啊WA啊的,原因你萌显然自己也清楚。

 

#include <bits/stdc++.h>using namespace std;const int maxn = 100 + 5;const double INF = 1e14;typedef long long LL;int n, H, W;struct Rec{    int h, w, dis, tag;    bool operator < (const Rec &other)const    {        if(dis != other.dis)    return dis < other.dis;        return tag < other.tag;    }}rec[maxn];double G[maxn][maxn];double calc(double x, double y){    return sqrt(x * x + y * y);}double dist(int x, int y){    if(x == 0)  return rec[y].dis;    LL xup = (LL)rec[x].dis + rec[x].h;    LL ydown = rec[y].dis;    if(rec[x].tag == rec[y].tag)    {        return ydown - xup;    }    else    {        if(xup >= ydown)    return W - rec[x].w - rec[y].w;        if(rec[x].w + rec[y].w >= W)  return ydown - xup;        else return calc(W - rec[x].w - rec[y].w, ydown - xup);    }}double d[maxn];bool inque[maxn];double spfa(int sg, int Vs){    for(int i = 1; i <= Vs; i++)    d[i] = INF;    memset(inque, 0, sizeof(inque));    queue<int>que;    d[sg] = 0;    que.push(sg);    inque[sg] = true;    while(que.size())    {        int cur = que.front();que.pop();        inque[cur] = false;        for(int i = 0; i <= Vs; i++)        {            if(i == cur)    continue;            if(d[cur] + G[cur][i] < d[i])            {                d[i] = d[cur] + G[cur][i];                if(!inque[i])                {                    inque[i] = true;                    que.push(i);                }            }        }    }}int main(){    freopen("street.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)    {        scanf("%d%d%d", &n, &H, &W);        for(int i = 1; i <= n; i++)        {            int h, w, dis, tag;            scanf("%d%d%d%d", &h, &w, &dis, &tag);            rec[i] = {h, w, dis, tag};        }        sort(rec + 1, rec + n + 1);        //init map        for(int i = 0; i <= n; i++)        {            for(int j = 0; j <= n; j++)            {                if(i == j)  G[i][j] = 0;                else    G[i][j] = INF;            }        }        for(int i = 0; i <= n; i++)        {            for(int j = i + 1; j <= n; j++)            {                G[i][j] = G[j][i] = dist(i, j);            }        }        spfa(0, n);        double ans = H;        for(int i = 1; i <= n; i++)        {            double temp = H - rec[i].dis - rec[i].h + d[i];            if(temp < ans)  ans = temp;        }        printf("%.6f\n", ans);    }    return 0;}

C - The Wall(最小顶点覆盖)

题意:

           一个n*m的区域,有n+m个防御工事,x=i(0<=i<=n-1)可以杀死i<=x < i+1里的所有敌人,以(n/2,y)(0<=y<=m-1)为圆心,n/2为半径的上半圆弧会对杀死前面距该圆弧距离不超过1的敌人(注意不能跨圆弧杀人),现在给出p个敌人的坐标,问至少需要开放多少防御工事可以消灭多少敌人。

思路:

           最小顶点覆盖几乎裸题。

#include <bits/stdc++.h>using namespace std;const int maxn = 10000 + 5;const double eps = 1e-7;int n, m, p;int used[200 + 5], match[200 + 5];vector<int> G[200 + 5];bool dfs(int v){    used[v] = 1;    for(int i = 0; i < G[v].size(); i++)    {        int u = G[v][i], w = match[u];        if(w < 0 || !used[w] && dfs(w))        {            match[v] = u;            match[u] = v;            return true;        }    }    return false;}int hungary(int V){    int res = 0;    memset(match, -1, sizeof(match));    for(int u = 0; u < V; u++)    {        if(match[u] < 0)        {            memset(used, 0, sizeof(used));            if(dfs(u))  res++;        }    }    return res;}struct node{    double x, y;}nodes[maxn];int sign(double x){    if(fabs(x) < eps)   return 0;    if(x > eps) return 1;    return -1;}double dist(double x1, double y1, double x2, double y2){    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));}bool check(int a, int b){    double r = 1.0 * n / 2;    double ox = r, oy = b;    if(sign(nodes[a].y - oy) <= 0)  return false;    double temp = dist(ox, oy, nodes[a].x, nodes[a].y);    if(sign(temp - r) != -1 && sign(temp - 2 * r) == -1)    return true;    return false;}int main(){    freopen("wall.in","r",stdin);    int T;    scanf("%d", &T);    while(T--)    {        scanf("%d%d%d", &n, &m, &p);        for(int i = 0; i < n + m; i++)  G[i].clear();        for(int i = 0; i < p; i++)        {            double x, y;            scanf("%lf%lf", &x, &y);            nodes[i] = {x, y};        }        for(int i = 0; i < p; i++)        {            int fx = nodes[i].x;            for(int j = m - 1; j >= 0; j--)            {                if(check(i, j))                {                    G[fx].push_back(n + j);                    break;                }            }        }        for(int i = 0; i < n; i++)        {            sort(G[i].begin(), G[i].end());            G[i].resize(unique(G[i].begin(), G[i].end()) - G[i].begin());        }        printf("%d\n", hungary(n));    }    return 0;}


D - Popcorn(组合数)

题意:

  n个不同的物品选m件,求方案数

思路:

  组合数裸题

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 20 + 5;LL C[maxn][maxn];int main(){    freopen("popcorn.in", "r", stdin);    for(int i = 0; i < maxn; i++)        C[i][0] = 1;    for(int i = 1; i < maxn; i++)    {        for(int j = 1; j <= i; j++)//!!!        {            C[i][j] = C[i - 1][j] + C[i - 1][j - 1];        }    }    int T, n, m;    scanf("%d", &T);    while(T--)    {        scanf("%d%d", &n, &m);        printf("%lld\n", C[n][m]);    }    return 0;}

E - Jumping(bfs)

题意:

  n个点,每个点有一个跳跃的距离d[i],i只能跳到i-d[i]和i+d[i]且不能跳出去,问每个顶点到n的跳的最少的次数。

思路:

  直接建图跑一个宽搜就行了,建图的时候反向建图,就只要跑一遍就够了。很常用的技巧。

 

#include <bits/stdc++.h>using namespace std;const int maxn = 1e5 + 5;const int INF = 0x3f3f3f3f;vector<int>G[maxn];int vis[maxn], d[maxn];void bfs(int n){    queue<int>que;    que.push(n);    d[n] = 0;    while(que.size())    {        int cur = que.front();que.pop();        if(vis[cur])    continue;        vis[cur] = 1;        for(auto to : G[cur])        {            if(d[to] > d[cur] + 1)            {                d[to] = d[cur] + 1;                que.push(to);            }        }    }}int main(){    freopen("jumping.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)    {        int n;        scanf("%d", &n);        //init        for(int i = 1; i <= n; i++) G[i].clear();        memset(vis, 0, sizeof(vis));        memset(d, INF, sizeof(d));        for(int i = 1; i <= n; i++)        {            int x;            scanf("%d", &x);            if(i - x >= 1)  G[i - x].push_back(i);            if(i + x <= n)  G[i + x].push_back(i);        }        bfs(n);        for(int i = 1; i <= n; i++)        {            printf("%d\n", d[i] == INF ? -1 : d[i]);        }    }    return 0;}


G - The Galactic Olympics(第二类斯特林数)

题意:

  n场不同的比赛,派k个人去,一个人可以参加多场比赛且至少参加一场,每场比赛只能且必须要有一个人参加,问方案数。

思路:

  第二类斯特林数。然后就好做了。

#include <bits/stdc++.h>using namespace std;const int maxn = 1e3 + 5;const int mod = 1e9 + 7;typedef long long LL;LL fac[maxn];LL dp[maxn][maxn];int main(){    freopen("galactic.in", "r", stdin);    for(int i = 0; i <= 1000; i++)   dp[i][i] = 1;    for(int i = 2; i <= 1000; i++)    {        for(int j = 1; j < i; j++)        {            dp[i][j] = 1LL * j * dp[i - 1][j] % mod + dp[i - 1][j - 1];            dp[i][j] %= mod;        }    }    fac[0] = 1;    for(int i = 1; i <= 1000; i++)  fac[i] = fac[i - 1] * i % mod;    int T;    scanf("%d", &T);    while(T--)    {        int games, people;        scanf("%d%d", &games, &people);        if(games < people)   puts("0");        else    printf("%lld\n", dp[games][people] * fac[people] % mod);    }    return 0;}




H - Commandos(dp)

题意:

  一栋楼有十层,(f,x,y)表示f层的x-y房间,每次可以用(f,x,y)移动到(f-1,x,y)或(f,x+1,y)或(f,x,y+1),初始在(10,1,1),现在给出一些房间中的人数,问最多可以带走多少人。

思路:

  。。水dp。

#include <bits/stdc++.h>using namespace std;int ma[15][15][15];int dp[15][15][15];int main(){    freopen("commandos.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)    {        memset(ma, 0, sizeof(ma));        int n;        scanf("%d", &n);        while(n--)        {            int f, x, y, h;            scanf("%d%d%d%d", &f, &x, &y, &h);            ma[f][y][x] = h;        }        for(int i = 10; i >= 1; i--)        {            for(int j = 1; j <= 10; j++)            {                for(int k = 1; k <= 10; k++)                {                    int choose = 0;                    choose = max(dp[i + 1][j][k], max(dp[i][j - 1][k], dp[i][j][k - 1]));                    dp[i][j][k] = choose + ma[i][j][k];                }            }        }        int ans = 0;        for(int i = 1; i <= 10; i++)        {            for(int j = 1; j <= 10; j++)            {                ans = max(ans, dp[1][i][j]);            }        }        printf("%d\n", ans);    }    return 0;}

I - On the way to the park(扫描线)

题意:

  给出一些小圆的圆心和半径,问一个圆心在x轴上,半径为R的大圆可以包含多少小圆的和最大为多少。

思路:

  可以把小圆,降到x轴上,(小圆和x轴的交点),然后类似扫描线的思路记录一下最大是多少就行了。

#include <bits/stdc++.h>using namespace std;const int maxn = 1e5+ 5;typedef long long LL;typedef pair<double, int>pdi;pdi a[2 * maxn];int main(){    freopen("walk.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)    {        int n, R;        scanf("%d%d", &n, &R);        int cnt = 0;        for(int i = 0; i < n; i++)        {            int x, y, r;            scanf("%d%d%d", &x, &y, &r);            if(r > R || abs(y) > R - r) continue;            double delta = sqrt(1.0 * (R - r) * (R - r) - 1.0 * y * y);            a[cnt++] = {-delta + 1.0 * x, r};            a[cnt++] = {delta + 1.0 * x, -r};        }        sort(a, a + cnt, [](pdi x, pdi y)             {                 if(x.first != y.first) return x.first < y.first;                 return x.second > y.second;             });        LL ans = 0, sum = 0;        for(int i = 0; i < cnt; i++)        {            sum += a[i].second;            ans = max(ans, sum);        }        printf("%lld\n", ans);    }    return 0;}



J - Whistle's New Car(dfs)

题意:

  给出一棵有向树,有点权和边权,定义一个节点i的魅力值,为以节点i为根的子树中有多少个节点j,节点j的点权不小于j->i的简单路径上边权和,求所有点的魅力值。

数据:

  n个节点,1<=n<=5e5,每个节点的点劝和每条边的边权范围都是1e9。

思路:

  我们处理一下,每一条链的情况,我们记录从根结点到结点cur的路径长度为depth[cur],那么,我们是不是可以知道这条路径上,cur结点贡献的魅力值,应该是从cur结点的父结点开始往上一直到这条链的第pos个结点,都+1,这个pos可以通过二分得到。然后是一个区间更新,区间更新用树dp的方式传递,pos的父结点更新一个-1,是要抵消掉传上来月以后下面cur的+1的更新,从而达到从pos结点到cur结点的父结点的+1的区间更新操作。

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 500000 + 5;struct node{    int to, w;};vector<node>G[maxn];int val[maxn], path[maxn], fa[maxn];LL depth[maxn], ans[maxn];void dfs(int cur, int pa, int num){    for(auto o : G[cur])  if(o.to != pa)    {        fa[o.to] = cur;        path[num] = o.to;        depth[num] = depth[num - 1] + o.w;        int pos = lower_bound(depth,  depth + num+ 1, depth[num] - val[o.to]) - depth;        if(pos < num)    ans[cur]++, ans[fa[path[pos]]]--;        dfs(o.to, cur, num + 1);        ans[cur] += ans[o.to];    }}int main(){    freopen("car.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)    {        int n;        scanf("%d", &n);        //init        for(int i = 1; i <= n; i++) G[i].clear();        memset(ans, 0, sizeof(ans));        for(int i = 1; i <= n; i++)        {            scanf("%d", &val[i]);        }        for(int i = 0; i < n - 1; i ++)        {            int u, v, w;            scanf("%d%d%d", &u, &v, &w);            G[u].push_back({v, w});            G[v].push_back({u, w});        }        path[0] = 1, depth[0] = 0;        dfs(1, -1, 1);        for(int i = 1; i <= n; i++)        {            printf("%lld%c", ans[i], i == n ? '\n' : ' ');        }    }    return 0;}



 

0 0
原创粉丝点击