ACM International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (ECPC 2015)

A.Arcade Game(康拓展开)

题意:

  给出一个每个数位都不同的数n,进行一场游戏。每次游戏将n个数的每个数位重组。如果重组后的数比原来的数大则继续游戏,否则算输。如果重组后的数是最大的数则算赢,问赢的概率。

题解:

  用康拓展开求出n是第几大的数,然后递推后面的概率。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
char s[15];
double ans;
int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
double cal(char *s) {
    int res = 0;
    int k = strlen(s);
    for(int i = 0; i < k; i++) {
        int cnt = 0;
        for(int j = i+1; j < k; j++) if(s[j]<s[i]) cnt++;
        res += fac[k-i-1]*cnt;
    } 
    if(res==fac[k]-1) return 0;
    double ans = 1.0/fac[k];
    for(int i = res; i < fac[k]-2; i++) ans += ans/fac[k];
    return ans;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%s", s);
        printf("%.9lf
", cal(s));
    }
}
View Code

B.Unlucky Teacher(模拟)

题意:

  Q个题目和M个学生的判卷,求出每道题的答案。如果求不出则输出?。

题解:

  模拟即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
int q, m;
int num[105], state[105];
char ans[105];
char s1[2], s2[2];
int main() {
    scanf("%d", &t);
    while(t--) {
        memset(state, 0, sizeof(state));
        memset(num, 0, sizeof(num));
        scanf("%d%d", &q, &m);
        while(m--) {
            for(int i = 1; i <= q; i++) {
                scanf("%s%s", s1, s2);
                if(s2[0]=='T') num[i] = -1, ans[i] = s1[0];
                else {
                    if((num[i]==-1)||((state[i]&(1<<s1[0]-'A'))>0)) continue;
                    state[i] |= 1<<s1[0]-'A';
                    num[i]++;
                    if(num[i]==3) {
                        for(int j = 0; j < 4; j++) 
                        if(!(state[i]&(1<<j))) {
                            ans[i] = 'A'+j;
                            break;
                        }
                        num[i] = -1;
                    }        
                }
            }
        }
        for(int i = 1; i <= q; i++) {
            if(num[i]>-1) printf("?");
            else printf("%c", ans[i]);
            if(i < q) printf(" ");
        }
        puts("");
    }
}
View Code

C.Connecting Graph(并查集+二分)

题意:

  初始有n个点,m次操作。每次操作加一条边或者询问两个点第一次连通的时刻(若不连通输出-1)。

题解:

  GYM - 100814 C.Connecting Graph

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int t;
int n, m;
int u, v, k;
int f[N], num[N];
vector<pair<int, int> > g[N];
vector<int> c[N];
bool check(int x) {
    int l = 0, r = g[u].size()-1;
    while(l <= r) {
        int mid = l+r>>1;
        if(g[u][mid].first <= x) l = mid+1;
        else r = mid-1;
    }
    int p1 = g[u][r].second;
    l = 0, r = g[v].size()-1;
    while(l <= r) {
        int mid = l+r>>1;
        if(g[v][mid].first <= x) l = mid+1;
        else r = mid-1;
    }
    int p2 = g[v][r].second;
    if(p1==p2) return 1;
    return 0;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            num[i] = 1;
            f[i] = i;
            c[i].clear();
            g[i].clear();
            c[i].push_back(i);
            g[i].push_back(make_pair(0, i));
        }
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d", &k, &u, &v);
            if(k&1) {
                u = f[u]; v = f[v];
                if(u!=v) {
                    if(num[u]>num[v]) swap(u, v);
                    for(int j = 0; j < num[u]; j++) {
                        c[v].push_back(c[u][j]);
                        f[c[u][j]] = v;
                        g[c[u][j]].push_back(make_pair(i, v));
                    }
                    num[v] += num[u];
                    num[u] = 0;
                    c[u].clear();
                }
            }
            else {
                int l = 0, r = i-1;
                while(l<=r) {
                    int mid = l+r>>1;
                    if(check(mid)) r = mid-1;
                    else l = mid+1;
                }
                if(check(r+1)) printf("%d
", r+1);
                else puts("-1");
            }
        }
    }    
}
View Code

D.Frozen Rivers

题意:

  一棵n个节点的树,每条边代表一条河。从点1开始边以每秒1个单位开始融化。每个点连的边(不包括连向父亲的)有一条融化完时剩下的该点连的边融化速度降为0.5。q次询问,每次询问某个时刻融化到叶子节点的数量。

题解:

  设minn[u]代表节点u的边中权值最小的那个,将点u所有边的权值加上他们与minn[u]的差值。即每条边的权值翻倍再减去minn[u]。

  这样处理完之后就省去了0.5的限制。问题变成了求叶子节点到根节点的距离。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int inf = 0x3f3f3f3f;
int t;
int n, q, p, c;
int tot;
int head[N], to[N], nxt[N], w[N], minn[N];
int cnt;
ll tim, num[N];
void dfs(int u, ll val) {
    if(minn[u]==inf) {
        num[++cnt] = val;
        return ;
    }
    for(int i = head[u]; ~i; i = nxt[i]) {
        w[i] = 2*w[i]-minn[u];
        dfs(to[i], val+w[i]);
    }
}
int main() {
    scanf("%d", &t);
    while(t--) {
        tot = cnt = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) head[i] = -1, minn[i] = inf;
        for(int i = 2; i <= n; i++) {
            scanf("%d%d", &p, &c);
            to[++tot] = i; nxt[tot] = head[p]; head[p] = tot, w[tot] = c;
            minn[p] = min(minn[p], c);
        }
        dfs(1, 0);
        sort(num+1, num+cnt+1);
        scanf("%d", &q);
        while(q--) {
            scanf("%lld", &tim);
            int ans = upper_bound(num+1, num+cnt+1, tim)-num-1;
            printf("%d
", ans);
        }
    }
}
View Code

E.Palmyra(dp)

题意:

  给出n*m的矩阵。从点(1,1)出发,可以向右或者向下移动,最后走到(n,m)。将路途上的点值乘起来,问最后的值拿6进制表示末尾最多有几个0。

题解:

  题意可以理解为,使最后2的因子数和3的因子数中的最小值最大。

  dp[i][j][k]表示走到(i,j),3的因子数为k时2的因子数最多是多少。

#include <bits/stdc++.h>
using namespace std;
int t;
int n, m;
int q[105][105][3];
int dp[105][105][1505];
int main() {
    scanf("%d", &t);
    while(t--) {
        memset(q, 0, sizeof(q));
        memset(dp, -1, sizeof(dp)); 
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                scanf("%d", &q[i][j][0]);
                int t = q[i][j][0];
                while(t%2 == 0) {
                    q[i][j][1]++;
                    t /= 2;
                }
                while(t%3 == 0) {
                    q[i][j][2]++;
                    t /= 3;
                }
            }
        }
        dp[1][1][q[1][1][2]] = q[1][1][1];
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                int n2 = q[i][j][1]; 
                int n3 = q[i][j][2]; 
                for(int k = 0; k + n3 <= 1500; k++) {
                    if(dp[i][j-1][k] != -1)
                        dp[i][j][k+n3] = max(dp[i][j][k+n3], dp[i][j-1][k]+n2);
                    if(dp[i-1][j][k] != -1)
                        dp[i][j][k+n3] = max(dp[i][j][k+n3], dp[i-1][j][k]+n2);
                }
            }
        }
        int ans = 0;
        for(int i = 1; i <= 1500; i++) {
            int nn = min(dp[n][m][i], i);
            ans = max(ans, nn);
        }
        printf("%d
", ans);
    }
    return 0;
}
View Code

F.Geometry

题意:

  给出长和宽,判断时正方形还是矩形。

题解:

  判断w是否等于h。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
int w, h;
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &w, &h);
        if(w==h) puts("Square");
        else puts("Rectangle");
    }
}
View Code

G.It is all about wisdom(最短路+二分)

题意:

  给出一个图,图中的每条边有使用的最低限制值和花费。问从1走到n在总花费小于k的前提下的最小限制值是多少。

题解:

  标准的二分套最短路的题型。二分最小的限制值即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int inf = 0x3f3f3f3f;
int t;
int n, m, k;
int u, v, c, l, r;
int vis[N], dis[N];
struct node {
    int to, v, lim;
    node(int a, int b, int c) {
        to = a; v = b; lim = c;
    }
};
vector<node> g[N];
bool check(int x) {
    queue<int> q;
    for(int i = 1; i <= n; i++) vis[i] = 0, dis[i] = inf;
    q.push(1);
    vis[1] = 1;
    dis[1] = 0;
    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].lim > x) continue;
            if(g[v][i].v+dis[v] < dis[g[v][i].to]) {
                dis[g[v][i].to] = g[v][i].v+dis[v];
                if(!vis[g[v][i].to]) {
                    q.push(g[v][i].to);
                    vis[g[v][i].to] = 1;
                }
            }
        }
    }
    if(dis[n] < k) return 1;
    return 0;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d", &n, &m, &k);
        r = 0;
        for(int i = 1; i <= n; i++) g[i].clear();
        while(m--) {
            scanf("%d%d%d%d", &u, &v, &c, &l);
            g[u].push_back(node(v, c, l));
            g[v].push_back(node(u, c, l));
            r = max(r, l);
        }
        l = 1;
        while(l<=r) {
            int mid = l+r>>1;
            if(check(mid)) r = mid-1;
            else l = mid+1;
        }
        if(check(r+1)) printf("%d
", r+1);
        else puts("-1");
        
    }
}
View Code

I.Salem

题意:

  给出n个数,求数对中最大的hamming距离。

题解:

  每两个数求一下异或之后二进制下1个数量即可,输出最大值。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n;
int a[105];
int main() {
    scanf("%d", &t);
    while(t--) {
        int ans = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            for(int j = 1; j < i; j++) {
                int p = a[i]^a[j], cnt = 0;
                for(int k = 20; k >= 0; k--) {
                    if(p&(1<<k)) cnt++;
                }
                ans = max(ans, cnt);
            }
        }
        printf("%d
", ans);
    }
}
View Code

J.Game

题意:

  给出合并规则表。两个人轮流进行操作,每次选择从最左面或者最右面开始每两个合并成一个。如果最后剩的是元音字符,就是Salah获胜。否则Marzo获胜。

题解:

  暴力维护每一种情况。用1表示S获胜,0表示M获胜。

  当S操作时,若两种情况存在1,则当前为1。

  当M操作时,若两种情况存在0,则当前为0。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4+10;
int t;
int tot;
int len[N];
char s[N][N];
char g[30][30];
char cmp[5] = {'a', 'e', 'i', 'o', 'u'};
bool dfs(int num, int k) {
    if(len[num] < 3) {
        char c;
        if(len[num]==1) c = s[num][0];
        else c = g[s[num][0]-'a'][s[num][1]-'a'];
        for(int i = 0; i < 5; i++) if(c==cmp[i]) return true;
        return false;
    }
    ++tot;
    for(int i = 0; i < len[num]; i+=2) {
        if(i==len[num]-1) s[tot][i/2] = s[num][i];
        else s[tot][i/2] =  g[s[num][i]-'a'][s[num][i+1]-'a'];
    }
    len[tot] = (len[num]+1)/2;
    bool res = dfs(tot, k^1);
    if(len[num]&1) {
        ++tot;
        s[tot][0] = s[num][0];
        for(int i = 1; i < len[num]; i+=2) {
            s[tot][i/2+1] =  g[s[num][i]-'a'][s[num][i+1]-'a'];
        }
        len[tot] = (len[num]+1)/2;
        if(k) res &= dfs(tot, k^1);
        else res |= dfs(tot, k^1);
    }
    return res;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        tot = 0;
        for(int i = 0; i < 26; i++) scanf("%s", g[i]);
        scanf("%s", s[0]);
        len[0] = strlen(s[0]);
        if(dfs(0, 0)) puts("Salah");
        else puts("Marzo");
    }
}
View Code

K.PhD math

题意:

  给出a,b,n,p(a<b)。求a/b的前n位数中有多少字串整除p。

题解:

  从1扫到n。维护每一位新增的余数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int t;
ll a, b;
int n, p;
int bit[N];
int v1[205], v2[205];
ll ans;
int main() {
    scanf("%d", &t);
    while(t--) {
        ans = 0;
        memset(v1, 0, sizeof(v1));
        memset(v2, 0, sizeof(v2));
        scanf("%lld%lld%d%d", &a, &b, &n, &p);
        for(int i = 1; i <= n; i++) {
            a *= 10;
            bit[i] = a/b;
            a = a%b;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j < p; j++) {
                if(i&1) v1[j] = 0;
                else v2[j] = 0;
            }
            for(int j = 0; j < p; j++) {
                if(i&1) v1[(j*10+bit[i])%p] += v2[j];
                else v2[(j*10+bit[i])%p] += v1[j];
            }
            if(i&1) v1[bit[i]%p]++, ans += v1[0];
            else v2[bit[i]%p]++, ans += v2[0];
        }
        printf("%lld
", ans);
    }
}
View Code

L.Candy Jars(博弈)

题意:

  N个罐子,每个罐子有一定数量的糖。两个人轮流操作,每次选定一罐,把其他罐中的糖都扔掉。然后把选定罐中的糖任意分配给每个罐,但要保证每个罐中都有糖。不能操作者判输。

题解:

  只要有一个罐子糖数必胜则操作者必胜。

  当所有罐子糖数小于N时无法给所有罐子分配糖,必输。

  当存在罐子糖数在[N,N(N-1)]时,可以把糖分成必输态,即分成所有罐子糖数小于N的状态,这时必胜。

  然后举例发现N(N-1)是一个循环节,取模就可以了。

#include <bits/stdc++.h>
using namespace std;
int t, n;
int k;
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &k);
            k %= n*(n-1);
            if(k==0 || k > n-1) ans = 1;
        }
        if(ans) puts("Alice");
        else puts("Bob");
    }
}
View Code

M.Building Force Fields(dp)

题意:

  按x升序给出n个点的二维坐标,并保证没有两个点x坐标相同。可以在任意两个点之间连边,最后要保证每个点都在连边之下(或在连边上)。问最小的连边总长。

题解:

  dp[i]表示第i个点结尾的最小总连边长。

  转移是枚举i向第j(1<=j<i)个点连边,要保证连边上方无点。即第i和第j个点的斜率比第i个点和(j,i)范围内的点的斜率都小。最后取最小值。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n;
double dp[1005];
struct node {
    ll x, y;
}a[1005];
double dis(int n1, int n2) {
    return sqrt((a[n1].x-a[n2].x)*(a[n1].x-a[n2].x)+(a[n1].y-a[n2].y)*(a[n1].y-a[n2].y));
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%lld%lld", &a[i].x, &a[i].y);
        dp[1] = dis(1, 2);
        for(int i = 2; i <= n; i++) {
            int pos = i-1;
            dp[i] = min(dp[i-2], dp[i-1])+dis(i, i-1);
            for(int j = i-2; j >= 1; j--) {
                if((a[i].y-a[pos].y)*(a[i].x-a[j].x) >= (a[i].y-a[j].y)*(a[i].x-a[pos].x)) {
                    dp[i] = min(dp[i], min(dp[j-1], dp[j])+dis(i, j));
                    pos = j;
                }
            }
        }
        printf("%.6lf
", dp[n]);
    }
}
View Code
原文地址:https://www.cnblogs.com/Pneuis/p/9130470.html