2016-2017 ACM-ICPC, Egyptian Collegiate Programming Contest (ECPC 16)

A.The game of Osho(sg函数+二项展开)

题意:

  一共有G个子游戏,一个子游戏有Bi, Ni两个数字。两名玩家开始玩游戏,每名玩家从N中减去B的任意幂次的数,直到不能操作判定为输。问谁最终能赢。

题解:

  GYM - 101147 A.The game of Osho

#include <bits/stdc++.h>
using namespace std;
int t;
int g;
int b, n;
int ans;
int main() {
    freopen("powers.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        ans = 0;
        scanf("%d", &g);
        while(g--) {
            scanf("%d%d", &b, &n);
            if(b&1) ans ^= n&1;
            else {
                int tt = n%(b+1);
                if(tt==b) ans ^= 2;
                else ans ^= tt&1;
            }
        }
        if(ans) puts("1");
        else puts("2");
    }
}
View Code

 

B.Street(最短路)

题意:

  大矩形代表市场,大矩形当中有很多小矩形样式的伞。这些小矩形都贴着大矩形的左边或者右边且互不相交。小矩形以外的地方都是阳光。求经过大矩形时在阳光下的最短时间。

题解:

  GYM - 101147 B.Street

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n, vis[105];
double L, U;
double diss[105];
struct rec {
    double h, w, d;
    int k;
}r[105];
struct node {
    int to;
    double val;
    node(int a, double b) {
        to = a; val = b;
    }
};
vector<node> g[105];
double distance(double x1, double y1, double x2, double y2) {
    return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
void add_edge(int u, int v, double val) {
    g[u].push_back(node(v, val));
    g[v].push_back(node(u, val));
}
void unite(int u, int v) {
    double dis;
    if(r[u].k==r[v].k || r[u].w+r[v].w > U) 
    dis = max(r[u].d-r[v].d-r[v].h, r[v].d-r[u].d-r[u].h);
    else {
        if(r[u].d > r[v].d+r[v].h) dis = distance(r[u].w, r[u].d, U-r[v].w, r[v].d+r[v].h);
        else if(r[v].d > r[u].d+r[u].h) dis = distance(r[v].w, r[v].d, U-r[u].w, r[u].d+r[u].h);
        else dis = U-r[u].w-r[v].w;
    }
    add_edge(u, v, dis);
}
void spfa(int s) {
    queue<int> q;
    q.push(s);
    memset(vis, 0, sizeof(vis));
    vis[s] = 1;
    while(!q.empty()) {
        int v = q.front();
        q.pop();
        vis[v] = 0;
        int len = g[v].size();
        for(int i = 0; i < len; i++) {
            if(g[v][i].val+diss[v] < diss[g[v][i].to]) {
                diss[g[v][i].to] = g[v][i].val+diss[v];
                if(!vis[g[v][i].to]) {
                    q.push(g[v][i].to);
                    vis[g[v][i].to] = 1;
                }
            }
        }
    }
}
int main() {
    freopen("street.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        scanf("%d%lf%lf", &n, &L, &U);
        for(int i = 0; i <= n+1; i++) {
            g[i].clear();
            if(i) diss[i] = 1e15;
        }
        for(int i = 1; i <= n; i++) scanf("%lf%lf%lf%d", &r[i].h, &r[i].w, &r[i].d, &r[i].k);
        for(int i = 1; i <= n; i++) {
            add_edge(0, i, r[i].d);
            add_edge(n+1, i, L-r[i].d-r[i].h);
        }
        for(int i = 1; i < n; i++)
        for(int j = i+1; j <= n; j++) 
            unite(i, j);
        spfa(0);
        printf("%.6lf\n", diss[n+1]);
    }
}
View Code

 

C.The Wall(二分图匹配)

题意: 

  长和宽分别为M+N/2,N的矩形中。有很多敌人的点。有两种方法消灭敌人。

  1.N个桶,第i个桶可以消灭i-1<=x<i中的敌人。2.M个摆(半圆)每个摆可以消灭距离他前面不超过1以内的敌人。第i个摆的圆心在(N/2,i-1),半径都为N/2。

  问消灭所有敌人消耗的最少设备是多少。

题解:

  GYM - 101147 C.The Wall

#include <bits/stdc++.h>
using namespace std;
int t;
int p;
double n, m;
int ans;
int vp[10005];
int vis[205];
int match[205];
struct ndoe {
    double x, y;
}w[10005];
double distance(double x1, double y1, double x2, double y2) {
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
vector<int> g[205];
bool dfs(int u) {
    for(int i = 0; i < g[u].size(); i++) {
        int v = g[u][i], w = match[v];
        if(vis[v]) continue;
        vis[v] = 1;
        if(w<0 || dfs(w)) {
            match[v] = u;
            return true;
        }
    }
    return false;
}
int hungarian() {
    int res = 0;
    memset(match, -1, sizeof(match));
    for(int u = 0; u < n; u++) {
        memset(vis, 0, sizeof(vis));
        if(dfs(u)) res++;
    }
    return res;
}
int main() {
    freopen("wall.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        ans = 0;
        memset(vp, 0, sizeof(vp));
        scanf("%lf%lf%d", &n, &m, &p);
        for(int i = 0; i < n+m; i++) g[i].clear();
        for(int i = 1; i <= p; i++) scanf("%lf%lf", &w[i].x, &w[i].y);
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= p; i++) {
            if(distance(n/2, 0, w[i].x, w[i].y)<=n/2 || (w[i].y>m && distance(n/2, m-1, w[i].x, w[i].y)>1.0+n/2)) {
                vp[i] = 1;
                int v = (int)floor(w[i].x);
                if(vis[v]) continue;
                else {
                    vis[v] = 1; ans++;
                }
            }
        }
        for(int i = 1; i <= p; i++) {
            if(vp[i]) continue;
            int judge = (int)floor(w[i].x);
            if(vis[judge]) continue;
            int up = (int)floor(w[i].y);
            for(int v = up; v >= 0; v--) {
                if(distance(n/2, (double)v, w[i].x, w[i].y) > n/2) {
                    int u = (int)floor(w[i].x);
                    g[u].push_back(v+n); 
                    break;
                }
            }
        }
        ans += hungarian();
        printf("%d\n", ans);
    }
}
View Code

 

D.Popcorn

题意:

  组合数签到题。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
int n, m;
int c[25][25];
int main() {
    freopen("popcorn.in","r",stdin);
    c[1][0] = c[1][1] = 1;
    for(int i = 2; i <= 20; i++) {
        c[i][0] = 1;
        for(int j = 1; j <= 20; j++) c[i][j] = c[i-1][j]+c[i-1][j-1];
    }
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        printf("%d\n", c[n][m]);
    }
}
View Code

 

E.Jumping

题意: 

  n个点,每个点有个值d[i],表示他能跳到i-d[i]和i+d[i]但不能越界。问每个点跳到点n的最小步数。若不能,输出-1。

题解:

  扫一遍,对于点i从点i-d[I]和i+d[i]向点i连单向边。最后从点n bfs一遍即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int t;
int n, tmp;
int vis[maxn];
int ans[maxn];
struct node {
    int id, val;
}temp;
vector<int> g[maxn];
int update(int id, int val) {
    temp.id = id; temp.val = val;
}
void bfs() {
    queue<node> q;
    update(n, 0);
    ans[n] = 0;
    q.push(temp);
    while(!q.empty()) {
        node u = q.front();
        q.pop();
        if(vis[u.id]) continue;
        vis[u.id] = 1;
        int len = g[u.id].size();
        for(int i = 0; i < len; i++) {
            int v = g[u.id][i];
            if(ans[v]==-1)
            ans[v] = u.val+1;
            else ans[v] = min(ans[v], u.val+1);
            update(v, u.val+1);
            q.push(temp);
        }
    }
}
int main() {
    freopen("jumping.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        memset(vis, 0, sizeof(vis));
        memset(ans, -1, sizeof(ans));
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) g[i].clear();
        for(int i = 1; i <= n; i++) {
            scanf("%d", &tmp);
            if(i-tmp > 0) g[i-tmp].push_back(i);
            if(i+tmp <= n) g[i+tmp].push_back(i);
        } 
        bfs();
        for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
    }
}
View Code

 

F.Bishops Alliance(树状数组)

题意:

  一个n*n的棋盘,有m个主教。每个主教都有自己的权值p。给出一个值C,在棋盘中找到一个最大点集。这个点集中的点在同一条对角线上且对于点集中任意两点(i,j),i和j之间的主教数(包括i,j)不小于pi^2+pj^2+C。

题解:

  GYM - 101147 F.Bishops Alliance

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, ll> P;
const int maxn = 1e5+10;
int t;
int n, m;
ll c;
int u, v;
ll p;
int cnt;
int ans;
ll _sort[maxn<<1];
int tree[maxn<<1];
struct node {
    int x;
    ll val;
    node(int a, ll b) {
        x = a; val = b;
    }
};
vector<P> bs[2][maxn<<1]; 
void update(int x, int val) {
    while(x <= cnt) {
        tree[x] = max(tree[x], val);
        x += x&(-x); 
    }
}
int sum(int x) {
    int res = 0;
    while(x > 0) {
        res = max(res, tree[x]);
        x -= x&(-x);
    }
    return res;
}
int main() {
    freopen("bishops.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        ans = 0;
        scanf("%d%d%lld", &n, &m, &c);
        for(int i = 0; i < 2; i++) 
        for(int j = 1; j < 2*n; j++) bs[i][j].clear();
        while(m--) {
            scanf("%d%d%lld", &u, &v, &p);
            p *= p;
            bs[0][u+v-1].push_back(P(u, p));
            bs[1][u-v+n].push_back(P(u, p));
        }
        for(int i = 0; i < 2; i++) 
        for(int j = 1; j < 2*n; j++) sort(bs[i][j].begin(), bs[i][j].end());
        
        for(int i = 0; i < 2; i++)
        for(int j = 1; j < 2*n; j++) {
            int len = bs[i][j].size();
            cnt = 0;
            for(int k = 0; k < len; k++) {
                int x = bs[i][j][k].first;
                ll p = bs[i][j][k].second;
                _sort[cnt++] = -p-x;
                _sort[cnt++] = p+c-x-1;
            }
            sort(_sort, _sort+cnt);
            cnt = unique(_sort, _sort+cnt)-_sort;
            for(int k = 1; k <= cnt; k++) tree[k] = 0;
            for(int k = 0; k < len; k++) {
                int x = bs[i][j][k].first;
                ll p = bs[i][j][k].second;
                int id = cnt-(lower_bound(_sort, _sort+cnt, p+c-x-1)-_sort);
                int tmp = sum(id);
                ans = max(ans, tmp+1);
                id = cnt-(lower_bound(_sort, _sort+cnt, -p-x)-_sort);
                update(id, tmp+1);
            }
        }
        printf("%d\n", ans);
    }
}
View Code

 

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

题意:

  N个游戏,K个孩纸。游戏之间,孩纸之间都互不相同。每个孩纸能玩多个游戏,每个游戏只能被一个孩纸玩且必须有人玩。求情况数。

题解:

  第二类斯特林数的基础上乘个k!.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
int t;
int n, k;
ll s[1005][1005];
int main() {
    freopen("galactic.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &k);
        if(n < k) {
            puts("0");
            continue;
        }
        memset(s, 0, sizeof(s));
        s[1][1] = 1;
        for(int i = 2; i <= n; i++) 
        for(int j = 1; j <= i; j++) s[i][j] = (s[i-1][j-1]+j*s[i-1][j])%mod;
        for(int i = k; i > 1; i--) s[n][k] = (s[n][k]*i)%mod;
        printf("%lld\n", s[n][k]);
    }
}
View Code

 

H.Commandos(dp水题)

题意:

  一名突击队员初始位于点(10,1,1)。当他位于点(F,Y,X)时,他可以移动到(F-1,Y,X)或(F,Y+1,X)或(F,Y,X+1)但不能越界。现在N个地点有一定的人质数。求他一次能救最多多少人质。

题解:

  dp水题。dp[i][j][k] += max(dp[i+1][j][k], max(dp[i][j-1][k], dp[i][j][k-1]))。

#include <bits/stdc++.h>
using namespace std;
int dp[15][15][15];
int main() {
    freopen("commandos.in","r",stdin);
    int t, n, f, y, x, h;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        memset(dp, 0, sizeof(dp));
        while(n--) {
            scanf("%d%d%d%d", &f, &y, &x, &h);
            dp[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++) {
                    dp[i][j][k] += max(dp[i+1][j][k], max(dp[i][j-1][k], dp[i][j][k-1]));
                }
            }
        }
        printf("%d\n", dp[1][10][10]);
    }
} 
View Code

 

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

题意:

  给出N个小圆的坐标及半径和一个大圆的半径。大圆的圆心位于x轴,问大圆最多能包含的小圆半径和最大为多少。

题解:

  有点像差分的思想。预处理出每个小圆的范围[L,R]即在该范围时小圆在大圆内。然后扫到L时-r,扫到R时+r。

  先-r后+r的原因是,排序的方式是如果第一关键字相等的话比较第二关键字。对于L=R的圆来说,肯定是先扫到-r,所以要负着来做。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<double, int> P;
const int maxn = 1e5+10;
int t;
int n, m;
int x, y, r;
ll sum, ans;
P spot[maxn<<1];
int main() {
    freopen("walk.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        sum = 0; ans = 0;
        scanf("%d%d", &n, &m);
        int cnt = 0;
        while(n--) {
            scanf("%d%d%d", &x, &y, &r);
            if(abs(y) > m-r) continue;
            double tmp = sqrt(1.0*(m-r)*(m-r)-1.0*y*y);
            spot[cnt++] = P(1.0*x-tmp, -r);
            spot[cnt++] = P(1.0*x+tmp, r);
        }
        sort(spot, spot+cnt);
        for(int i = 0; i < cnt; i++) {
            sum += spot[i].second;
            if(ans < -sum) ans = -sum;
        }
        printf("%lld\n", ans);
    }
}
View Code

 

J.Whistle's New Car(倍增+差分) 

题意:

  给出一颗有点权和边权的树。求每一个点u的子树中有多少点v,使得点v到点u的距离小于等于点v的权值。

题解:

  GYM - 101147 J.Whistle's New Car

#include <bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int maxn = 5e5+10;
int t, n, u, v, tree[maxn], ans[maxn];
ll c, x[maxn];
struct node {
    ll val; int nod;
};
node fa[32][maxn], tmp;
vector<node> g[maxn];
void dfs(int u, int pre) {
    fa[0][u].nod = pre;
    int len = g[u].size();
    for(int i = 0; i < len; i++) {
        int v = g[u][i].nod;
        if(v==pre) {
            fa[0][u].val = g[u][i].val;
            continue;
        }
        dfs(v, u);
    }
}
void dfs_double(int u, int pre) {
    int len = g[u].size();
    for(int i = 0; i < len; i++) {
        int v = g[u][i].nod;
        if(v==pre) continue;
        dfs_double(v, u);
    }
    tree[u]++;
    ll tv = x[u];
    for(int k = 31; k >= 0; k--) {
        if(fa[k][u].val <= tv) {
            tv -= fa[k][u].val;
            u = fa[k][u].nod;
        }
    }
    tree[u]--;
}
void solve(int u, int pre) {
    int len = g[u].size();
    for(int i = 0; i < len; i++) {
        int v = g[u][i].nod;
        if(v==pre) continue;
        solve(v, u);
    }
    for(int i = 0; i < len; i++) {
        int v = g[u][i].nod;
        if(v==pre) continue;
        ans[u] += tree[v]+ans[v];
    }
}
int main() {
    freopen("car.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        memset(tree, 0, sizeof(tree));
        memset(ans, 0, sizeof(ans));
        for(int i = 1; i <= n; i++) g[i].clear();
        for(int i = 1; i <= n; i++) scanf("%lld", &x[i]);
        for(int i = 1; i < n; i++) {
            scanf("%d%d%lld", &u, &v, &tmp.val);
            tmp.nod = v;
            g[u].push_back(tmp);
            tmp.nod = u;
            g[v].push_back(tmp);
        }
        dfs(1, 1);
        for(int k = 0; k < 31; k++) {
            for(int v = 1; v <= n; v++) { 
                fa[k+1][v].nod = fa[k][fa[k][v].nod].nod;
                fa[k+1][v].val = fa[k][v].val+fa[k][fa[k][v].nod].val;
            }
        }
        dfs_double(1, 0);
        solve(1, 0);
        for(int i = 1; i <= n; i++) {
            printf("%d", ans[i]);
            if(i==n) puts("");
            else printf(" ");
        }
    }
}
View Code

 

K.Touristic Trip(概率DP+条件概率)

题意:

  一个人从城市0开始旅行。一共有N座城市,他每到一座城市都会寄一张明信片。给出从一座城市到另一座城市的概率和在每座城市寄出每张明信片的概率。给出长度为k的寄明信片的序列。问在该序列的条件下在第Z个城市寄出第Q张明信片的序列。

题解:

  GYM - 101147 K.Touristic Trip

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t; 
int n, m, k, q, z;
double p[25][25], c[25][15];
double dp[20][25], dpp[20][25];
int a[20];
int main() {
    freopen("trip.in","r",stdin);
    scanf("%d", &t);
    while(t--) {
        memset(dp, 0, sizeof(dp));
        memset(dpp, 0, sizeof(dpp));
        scanf("%d%d%d%d%d", &n, &m, &k, &q, &z);
        for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++) scanf("%lf", &p[i][j]);
        for(int i = 0; i < n; i++) 
        for(int j = 0; j < m; j++) scanf("%lf", &c[i][j]);
        for(int i = 0; i < k; i++) scanf("%d", &a[i]);
        dp[0][0] = c[0][a[0]]; 
        for(int i = 1; i <= q; i++) {
            for(int j = 0; j < n; j++) 
            for(int k = 0; k < n; k++)
            dp[i][j] += dp[i-1][k]*p[k][j]*c[j][a[i]];
        }
        dpp[q][z] = dp[q][z];
        for(int i = q+1; i < k; i++) {
            for(int j = 0; j < n; j++) 
            for(int k = 0; k < n; k++) {
                dp[i][j] += dp[i-1][k]*p[k][j]*c[j][a[i]];
                dpp[i][j] += dpp[i-1][k]*p[k][j]*c[j][a[i]];
            }
        }
        for(int i = 1; i < n; i++) {
            dp[k-1][i] += dp[k-1][i-1];
            dpp[k-1][i] += dpp[k-1][i-1];
        }
        printf("%.3lf\n", dpp[k-1][n-1]/dp[k-1][n-1]);
    }
}
View Code

 

转载于:https://www.cnblogs.com/Pneuis/p/8902246.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值