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

题目链接:http://codeforces.com/gym/101147

2017/8/27日训练赛,题目情况9/11,Rank 4/79。

A. The game of Osho

题意:定义一个子游戏,B,N可以从N减掉B^k要求B^k小于等于N,当N变成0,该哪个人选,哪个人就输了,给出G个这样的游戏,问最后的输赢情况。

解法:组合游戏,SG打表发现规律,然后求Nim和判断即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//int cal(LL b, LL g){
//    if(g==0) return 0;
//    bool h[10];
//    memset(h,0,sizeof(h));
//    LL bb=1;
//    while(bb<=g){
//        int t=cal(b,g-bb);
//        if(t<10) h[t]=1;
//        if(b==1) break;
//        bb*=b;
//    }
//    for(int x=0;x<10; x++)
//        if(!h[x]) return x;
//}
int main()
{
//    for(int x=1; x<=5; x++){
//        for(int y=1; y<=5; y++){
//            printf("%d %d %d\n", x,y,cal(x,y));
//        }
//    }
    freopen("powers.in","r",stdin);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n;
        scanf("%d", &n);
        int ans = 0;
        for(int i=0; i<n; i++){
            int b,g;
            scanf("%d%d",&b,&g);
            if(b&1) ans^=g&1;
            else{
                if(g%(b+1)<=b-1) ans^=(g%(b+1))&1;
                else ans^=2;
            }
        }
        if(ans) puts("1");
        else puts("2");
    }
    return 0;
}

 B. Street

题意:给了一个平面,然后给了一些矩形有靠左边界的,也有靠着右边界的,现在问要从高度为0跑到高度问n+1,最少需要多少距离,你在矩形里面和边缘走是不需要时间的,题目给的是矩形的长宽,和离底面的距离和往哪靠。

解法:讨论两个矩形的距离,这里要分3种情况,然后建图跑最短路,Floyd T了,改成Dij AC。

队友代码:

 

C. The Wall

留坑。

D. Popcorn

题意:求n个人选k个人的方法数。

解法:就是C(n,k)。

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

 E. Jumping

题意:一个人可以从1-n的任意位置出发,每个位置有一个d[i]表示这个人在这个位置可以向左跳d[i],也可以向右调d[i],问这个人从每个位置出发最后到达n的步数,不能到达输出-1。

解法:我们直接把n点看成起点,做一个最短路或者BFS,就可以求出N点到每个点的最短路了,不能到达就是dis[i]=INF的时候。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+2;
int dis[maxn];
bool f[maxn];
vector <int> G[maxn];
int main()
{
    freopen("jumping.in","r",stdin);
    int T,n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i=1; i<=n; i++) G[i].clear();
        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);
            }
        }
        memset(dis, 0x3f, sizeof(dis));
        memset(f, 0, sizeof(f));
        dis[n] = 0;
        queue <int> q;
        q.push(n);
        f[n] = 1;
        while(q.size()){
            int u = q.front();
            q.pop();
            for(int v : G[u]){
                if(!f[v]){
                    dis[v] = dis[u]+1;
                    f[v] = 1;
                    q.push(v);
                }
            }
        }
        for(int i=1; i<=n; i++){
            if(dis[i] > n) dis[i] = -1;
            printf("%d\n", dis[i]);
        }
    }
    return 0;
}

 F. Bishops Alliance

题意:给了一个n*n的棋盘(n<=100000),然后棋盘上有m个皇后,问把这些皇后放到一个对角线上,当且仅当a[i]和a[j]的横坐标之差between i and j is at least equal to pi2 + pj2 + C描述能力捉急),a[i]和a[j]才可以放到一个对角线上。问最多可以放的象棋的个数。

解法:把一条对角线的棋子取出来之后,就是判断满足这个关系的棋子的个数,可以发现这个类似于LIS的DP,dp[i] = max(dp[j])+1, 其中j<=x - p[i]*p[i] - C +1,然后求出最大值后再更新到x+p[i]*p[i]的位置就可以 了。

复杂度:O(nlogn)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long LL;
struct node{
    int x,y,p;
    node(){}
    node(int x,int y,int p):x(x),y(y),p(p){}
    bool operator < (const node &rhs) const{
        return x<rhs.x;
    }
}a[maxn],b[maxn];
int C;
namespace BIT{
    int c[maxn];
    void init(){
        memset(c, 0, sizeof(c));
    }
    inline int lowbit(LL x){
        return x&-x;
    }
    void add(LL pos, int val){
        for(LL i=pos; i<maxn; i+=lowbit(i))
            c[i] = max(c[i], val);
    }
    void Clear(LL pos){
        for(LL i=pos; i<maxn; i+=lowbit(i))
            c[i] = 0;
    }
    int query(LL pos){
        pos = min(pos, (LL)maxn-1);
        int ret = 0;
        for(LL i = pos; i>0; i-=lowbit(i))
            ret = max(ret, c[i]);
        return ret;
    }
}
using namespace BIT;
vector <int> v1[maxn*2];
vector <int> v2[maxn*2];
int cal(vector <int> &v){
    int n = v.size();
    if(n == 0) return 0;
    for(int i=0; i<n; i++) b[i] = a[v[i]];
    sort(b, b+n);
    int ret = 0;
    for(int i=0; i<n; i++){
        LL pos = b[i].x - (LL)b[i].p*b[i].p - C + 1;
        int d = query(pos)+1;
        ret = max(ret, d);
        pos = b[i].x + (LL)b[i].p*b[i].p;
        add(pos, d);
    }
    for(int i=0; i<n; i++){
        LL pos = b[i].x + (LL)b[i].p*b[i].p;
        Clear(pos);
    }
    return ret;
}
int main()
{
    freopen("bishops.in","r",stdin);
    int T; scanf("%d", &T);
    while(T--){
        int n,m;
        scanf("%d %d %d", &n,&m,&C);
        init();
        for(int i=0; i<=n*2; i++){
            v1[i].clear();
            v2[i].clear();
        }
        for(int i=1; i<=m; i++){
            scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].p);
            v1[a[i].x+a[i].y].push_back(i);
            v2[a[i].x-a[i].y+n].push_back(i);
        }
        int ans = 0;
        for(int i=0; i<=n*2; i++){
            ans = max(ans, cal(v1[i]));
            ans = max(ans, cal(v2[i]));
        }
        printf("%d\n", ans);
    }
    return 0;
}

 G. The Galactic Olympics

题意:抽象出来的题意:将p个物体划分成k个非空的可辨别的(可以理解为盒子有编号)集合的方法数。

解法:这个就是典型的第二类斯特林数。

第二类Stirling数 S(p,k)

   
S(p,k)的一个组合学解释是:将p个物体划分成k个非空的不可辨别的(可以理解为盒子没有编号)集合的方法数。

k!S(p,k)是把p个人分进k间有差别(如:被标有房号)的房间(无空房)的方法数。

   
S(p,k)的递推公式是:S(p,k)=k*S(p-1,k)+S(p-1,k-1) ,1<= k<=p-1

边界条件:S(p,p)=1 ,p>=0    S(p,0)=0 ,p>=1

 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int dp[3005][3005];
const int mod = 1e9+7;

int main()
{
    freopen("galactic.in", "r", stdin);
    dp[1][1] = 1;
    for(int i=2; i<=3000; i++){
        dp[i][i] = 1;
        for(int j=1; j<=i; j++){
            dp[i][j] = ((LL)j*dp[i-1][j]+dp[i-1][j-1])%mod;
        }
    }
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, k;
        scanf("%d %d", &n,&k);
        if(k > n){
            puts("0");
        }
        else{
            int ans = dp[n][k];
            for(int i=1; i<=k; i++)
                ans = (LL)ans*i%mod;
            ans %= mod;
            printf("%d\n", ans);
        }
    }
    return 0;
}

 H. Commandos

题意:给了一个3维的楼房,里面有些地方有人质,现在警察在第10层(1,1)的位置,给了这些警察可以行走的限制,问最多可以救出多少人质。

解法:就是3维的数塔问题,直接DP即可。

#include <bits/stdc++.h>
using namespace std;
int dp[12][12][12];
int val[12][12][12];

int main()
{
    freopen("commandos.in", "r", stdin);
    int T, n;
    scanf("%d", &T);
    while(T--){
        memset(dp, 0, sizeof(dp));
        memset(val, 0, sizeof(val));
        scanf("%d", &n);
        for(int i=0; i<n; i++){
            int f,x,y,h;
            scanf("%d %d %d %d", &f,&x,&y,&h);
            val[f][x][y] += h;
        }
        for(int f=10; f>=1; f--){
            for(int x=1; x<=10; x++){
                for(int y=1; y<=10; y++){
                    dp[f][x][y] = max(dp[f][x][y], dp[f+1][x][y]);
                    dp[f][x][y] = max(dp[f][x][y], dp[f][x-1][y]);
                    dp[f][x][y] = max(dp[f][x][y], dp[f][x][y-1]);
                    dp[f][x][y] += val[f][x][y];
                }
            }
        }
        printf("%d\n", dp[1][10][10]);
    }
    return 0;
}

 I. On the way to the park

题意:平面上给了一些圆的信息,然后给了一个半径R,就是你可以确定一个大圆R,这个大圆是在X轴上移动的,问这个大圆最多包含多少小圆,求这些小圆的半径和。

解法:我们把那些不可能被包含的小圆忽略,然后让剩下的小圆和大圆作相切,可以求出在什么范围内大圆可以包含这个小圆,然后把左端点打一个-r[i]的标记右端点打一个r[i]标记,排序之后求和并且更新最大值就是答案了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
typedef long long LL;
int n,m;
int x[maxn],y[maxn],r[maxn];
pair <double,int> a[maxn*3];

int main()
{
    freopen("walk.in","r",stdin);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n,&m);
        int cnt = 0;
        for(int i=1; i<=n; i++){
            scanf("%d %d %d", &x[i],&y[i],&r[i]);
            if(y[i]<0) y[i]=-y[i];
            if(y[i]+r[i]>m) continue;
            int d = m-r[i];
            double dx=sqrt(1.0*d*d-1.0*y[i]*y[i]);
            a[++cnt] = make_pair(x[i]-dx, -r[i]);
            a[++cnt] = make_pair(x[i]+dx, r[i]);
        }
        LL sum=0,ans=0;
        sort(a+1,a+cnt+1);
        for(int i=1; i<=cnt; i++){
            sum += -a[i].second;
            ans = max(ans, sum);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 J. Whistle's New Car

题意:给了n个点的图,图上每个点代表一个城市,并且拥有x[i]的值表示在这个城市可以加x[i]的油,定义城市i的吸引度为i有多少个子节点,只有在这个节点加油并且能跑到i点的节点个数,两个城市之间有边权代表路程。

解法:考虑到i点向上最远可以跑到哪个城市,在这个城市打上一个-1的标记,并且在当前点打上+1的标记,然后回溯的时候累加标记就可以 了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+10;
typedef long long LL;
vector <pair<int,int> > G[maxn];
int x[maxn];
int dep[maxn];
int stk[maxn];
int ans[maxn];
void dfs(int u, int fa, int d){
    stk[d] = u;
    int L = -1, R = d;
    while(L < R){
        int mid = (L+R+1)/2;
        if(dep[stk[mid]]>=dep[u]-x[u]) R=mid-1;
        else L=mid;
    }
    if(L>=0) --ans[stk[L]];
    ++ans[u];
    for(auto &p : G[u]){
        int v = p.first;
        if(v == fa) continue;
        dep[v] = dep[u] + p.second;
        dfs(v, u, d+1);
        ans[u] += ans[v];
    }
}

int main()
{
    freopen("car.in", "r", stdin);
    int T,n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i=1; i<=n; i++){
            G[i].clear();
            scanf("%d", &x[i]);
            ans[i]=0;
        }
        for(int i=1; i<n; i++){
            int a,b,c;
            scanf("%d%d%d", &a,&b,&c);
            G[a].emplace_back(b,c);
            G[b].emplace_back(a,c);
        }
        dfs(1,0,0);
        for(int i=1; i<n; i++){
            printf("%d ", ans[i]-1);
        }
        printf("%d\n", ans[n]-1);
    }
    return 0;
}

 K. Touristic Trip

留坑。

posted @ 2017-08-28 16:45  zxycoder  阅读(555)  评论(0编辑  收藏  举报