Codeforces Beta Round #69 Div1

以前做比赛的方法有些不对,只顾做比赛,平时也没做题,比赛做过之后就不管了,这样做不到学习的目的,只是练个手熟罢了,因此以后参加的比赛,要认真做好赛后AK的工作。

A. Heroes

题意大致这样,有7个人,要分成3组,去打Boss,对于三组有经验(A, B, C),存在这样的关系,a like b,要分组使得7个人每个人获得的经验差值就小,然后要使得3组中,like关系数最多。

暴力枚举7个人的分组情况,然后统计上述参数就可以了,复杂度O(3^7 * 7 * 7),联想到三进制进行枚举会较好实现,我用DFS去枚举分组情况。

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stdio.h>
#include <math.h>
using namespace std;
 
map<string, int> name;
int m_like[7][7];
int score[3];
int man[7];
int min_dif, max_val;
 
void ready()
{
    name["Anka"] = 0;
    name["Chapay"] = 1;
    name["Cleo"] = 2;
    name["Troll"] = 3;
    name["Dracul"] = 4;
    name["Snowy"] = 5;
    name["Hexadecimal"] = 6;
 
    memset(m_like, 0, sizeof(m_like));
 
    min_dif = INT_MAX;
    max_val = 0;
}
 
void dfs(int beg)
{
    if(beg == 7)
    {
        int num[3] = {0};
        for(int i = 0; i < 7; i++)
            num[man[i]]++;
        for(int i = 0; i < 3; i++)
            if(num[i] == 0)  return;
 
        int m_dif = 0;
        for(int i = 0; i < 3; i++)  for(int j = 0; j < 3; j++)
        {
            int a = int(score[i] / num[i]);
            int b = int(score[j] / num[j]);
            if(abs(a - b) > m_dif)
                m_dif = abs(a - b);
        }
 
        int m_val = 0;
        for(int i = 0; i < 7; i++)  for(int j = 0; j < 7; j++)
        {
            if(m_like[i][j] && man[i] == man[j])
                m_val++;
        }
 
        //printf("d: %d %d\n", m_dif, m_val);
 
        if(m_dif < min_dif)
        {
            min_dif = m_dif;
            max_val = m_val;
        }
        else if(m_dif == min_dif && m_val > max_val)
        {
            max_val = m_val;
        }
 
        return;
    }
    for(int i = 0; i < 3; i++)
    {
        man[beg] = i;
        dfs(beg + 1);
    }
}
 
int main()
{
    ready();
    int n;
    scanf("%d", &n);
    while(n--)
    {
        string a, like, b;
        cin >> a >> like >> b;
        m_like[name[a]][name[b]] = 1;
    }
    scanf("%d%d%d", &score[0], &score[1], &score[2]);
 
    /*
    for(int i = 0; i < 7; i++)
    {
        for(int j = 0; j < 7; j++)
            printf("%d ", m_like[i][j]);
        printf("\n");
    }
    */
 
    dfs(0);
 
    printf("%d %d\n", min_dif, max_val);
}

B. Falling Anvils

p, q是实数,p会取[0, a]中的任意值,q的范围是[-b, b],为使x^2+sqrt(p)*x+q=0至少有一个根的(p,q)取法概率是多少。

要使Delta>=0,即p-4q>=0,然后p,q的取值形成一个矩形,然后就可以求概率了,注意p=0,q=0的情况,当时晕晕的,想起来真恶心,三角形面积忘除以2等等,具体的看下图就明白了。

B

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stdio.h>
#include <math.h>
using namespace std;
 
double go(double p, double q)
{
    double q1 = p / 4;
    double p1 = q * 4;
    if(q1 <= q)  return (p * q1 + p * q * 2) / (4 * p * q);
    else return 1 - (q * p1 / 2) / (2 * p * q);
}
 
int main()
{
    /*
    for(int i = 1; i <= 10; i++)
    {
        for(int j = 1; j <= 10; j++)
            printf("%.6lf ", go(i, j));
        printf("\n");
    }
    */
 
    int T;
    scanf("%d", &T);
    while(T--)
    {
        double one = 1;
        double zero = 0;
        int p, q;
        scanf("%d%d", &p, &q);
        if(q == 0 && p == 0)  printf("%.10lf\n", one);
        else if(q == 0 && p != 0)  printf("%.10lf\n", one);
        else if(p == 0 && q != 0)  printf("0.5\n");
        else printf("%.10lf\n", go(p, q));
    }
}

C. Beavermuncher-0xFF

这题比较有意思,给一颗树,N个节点(N<=10^5),给定一个起点S,每个节点上有个参数记做Val(u),表示节点u上有Val(u)个果子,然后从S开始移动,假设一个移动从S->T,则T上的果子少掉一个(被吃掉了),如果T处没有果子,是不可以移动到这里的,然后问说从S开始移动,最多可以吃掉多少个果子。

这题的解法有点贪心的思想,想这题的时候,感觉很混乱,因为可以吃来吃去的,显得很无序,看了solution之后,把这些无序的过程整理的十分有序,并且很好的解决了这个问题,对于从一个地方吃到两一个地方,然后再吃回去,其实可以通过上面的过程去统一的,也就是可以递归下去,有子问题这个性质(这里不能给出严谨的说明),对于当前节点u,下面的儿子记做v1,v2,v3…vn,dfs(u)记做以u为父节点的子树,吃完后返回u吃的做多的果子数,同时记录下left(u)表示吃完最多后剩下的果子数,求dfs(u)的过程是,先对u的儿子儿子全部求一遍dfs,然后把儿子的最大果子数排一下序,从大到小开始吃,如果吃完后u处还有剩下果子,就对儿子的余下的果子,依次吃,这时只能吃儿子的根,因为如果能往下再吃的话,那么刚才就吃掉了,当然这里不能模拟,求下min(left(u), left(vi))就可以了,最后的复杂度是O(NlogN)

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stdio.h>
#include <math.h>
using namespace std;
 
const int MAX = 100005;
 
typedef __int64 int64;
 
vector<int> tree[MAX];
int n, val[MAX];
 
void dfs(int f, int u, int64& max_get, int& left)
{
    vector<int> v_left;
    vector<int64> v_get;
 
    for(int i = 0; i < tree[u].size(); i++)
    {
        int v = tree[u][i];
        if(v != f && val[v])    //val[v]要有值才可达
        {
            int c_left = val[v] - 1;
            int64 c_get;
            dfs(u, v, c_get, c_left);
 
            v_get.push_back(c_get);
            v_left.push_back(c_left);
        }
    }
 
    sort(v_get.begin(), v_get.end());
 
    max_get = 0;
 
    for(int i = v_get.size() - 1; left && i >= 0; i--)
    {
        left--;
        max_get += v_get[i] + 2;    
    }
 
    for(int i = 0; i < v_left.size() && left; i++)
    {
        int t = min(left, v_left[i]);
        left -= t;
        max_get += 2 * t; 
    }
 
    //printf("%d get %I64d left %d\n", u, max_get, left);
 
}
 
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &val[i]);
 
    for(int i = 1; i < n; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        tree[a].push_back(b);
        tree[b].push_back(a);
    }
 
    int root;
    scanf("%d", &root);
 
    int64 ans;
    dfs(0, root, ans, val[root]);
 
    printf("%I64d\n", ans);
}

D. Domino Carpet

问题是有个N*M的矩阵,要对它进行1*2的子集完全划分,有一定的规则可以使得两个元素放在一起,然后求合理的方法数,最后模1e9+7。

不得不说,题目读的很痛苦,怪自己英语没到家,大牛们都读的很快。因为这题有个特别的限制,就是如果有一个1*2的划分,那么这N*2列的元素中不能有其他的1*2的小方块,横着插进来,然后这题就可以用DP做了。

Dj = Dj-2 * Pj-1 + Dj-1 * Qj - Dj-2 * Qj-1 * Qj, D0=1, D1=Q1,Dj表示前j列的合法数,Pj表示第j和第j+1列这两列的合法数,Qj表示第j列的合法数(0 or 1)。

其实,前j列的排列数,可以按最后两列的情况来划分,一个是最后两列中有横着的,一个是最后两列中没有横着的,后者好处理,通过Dj-1来转移,前者直接处理不好处理,因此先求Pj-1,然后会有全部都是竖着的情况是和前者重复的,要减去,因此有了转移方程中的第三项。对于P,Q按着行,从上往下DP下就可以求出了。

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stdio.h>
#include <math.h>
using namespace std;
 
const int MAX = 255;
const int M = 1e9 + 7;
 
typedef __int64 int64;
 
int n, m, domi[MAX][MAX];
int64 P[MAX][MAX], Q[MAX][MAX];
int64 D[MAX];
char mm[5 * MAX][5 * MAX];
 
void Read()
{
    scanf("%d%d", &n, &m);
    for(int i = 0; i < 4 * n + 1; i++)
        scanf("%s", mm[i]);
}
 
int CanX(int a, int b)
{
    if(a != 2 && a != 3 && a != 6 &&
        b != 2 && b != 3 && b != 6) 
        return 1;
    else  return 0;
}
 
int CanY(int a, int b)
{
    if(a < 7 && b < 7)  return 1;
    else  return 0;
}
 
void Pre()
{
    //domi
    for(int i = 0; i < n; i++)  for(int j = 0; j < m; j++)
    {
        int bi = 4 * i + 1;
        int bj = 4 * j + 1;
 
        int p_num = 0;
        for(int di = 0; di < 3; di++)  for(int dj = 0; dj < 3; dj++)
        {
            if(mm[bi + di][bj + dj] == 'O')  p_num++;
        }
 
        
        if(p_num == 2 && mm[bi][bj] == 'O')  p_num += 7;
        else if(p_num == 3 && mm[bi][bj] == 'O')  p_num += 7;
        else if(p_num == 6 && mm[bi][bj + 1] == 'O')  p_num += 7;
        domi[i + 1][j + 1] = p_num;
    }
 
    /*
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
            printf("%d ", domi[i][j]);
        printf("\n");
    }
    */
 
    //Q
    for(int j = 1; j <= m; j++)
    {
        Q[0][j] = 1;
        Q[1][j] = 0;
 
        for(int i = 2; i <= n; i++)
            Q[i][j] = Q[i - 2][j] * CanY(domi[i][j], domi[i - 1][j]);
    }
 
    //P
    for(int j = 1; j < m; j++)
    {
        P[0][j] = 1;
        P[1][j] = CanX(domi[1][j], domi[1][j + 1]);
 
        for(int i = 2; i <= n; i++)
        {
            P[i][j] = ( P[i - 1][j] * CanX(domi[i][j], domi[i][j + 1]) +
                P[i - 2][j] * CanY(domi[i - 1][j], domi[i][j]) *
                CanY(domi[i - 1][j + 1], domi[i][j + 1]) ) % M;
        }
    }
}
 
 
 
void Solve()
{
    D[0] = 1;
    D[1] = Q[n][1];
    
    for(int j = 2; j <= m; j++)
    {
        D[j] = ( D[j - 2] * P[n][j - 1] + 
            D[j - 1] * Q[n][j] -
            D[j - 2] * Q[n][j - 1] * Q[n][j] ) % M;
    }
 
    printf("%I64d\n", (D[m] + M) % M);
}
 
int main()
{
    Read();
    Pre();
    Solve();
}

E. Martian Food

这是个经典问题,The Shoemaker's Knife

这道几何题考的比较偏僻,但是很有意思,让人学到了不少的东西,认识到了广义圆等等,通过另一篇随笔进行详细的介绍。这里大致讲下思路,用到了反演几何的知识,圆和直线有相似的特性,这里的圆的反演类比与点关于直线的对称,这样子,直线和圆反演后就变成直线和圆了,然后关于圆的位置关系等,就可以通过反演后方便的得到了,当然这要靠它的特性了,下图中,两个半径为r,R的圆反演成直线,然后要求的圆在两条直线的中间,然后求的第K个圆的位置后,再反演回来就行,关于反演可以通过极角坐标系很好的解释下变换,角度没变,半径求倒数。

E

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stdio.h>
#include <math.h>
using namespace std;
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        double R, r;
        int k;
        scanf("%lf%lf%d", &R, &r, &k);
 
        double A = 1 / (2 * R);
        double B = 1 / (2 * r);
        
        double ox = (A + B) / 2;
        double oy = k * (B - A);
 
        double or = (B - A) / 2;
        double ok = sqrt(ox * ox + oy * oy);
 
        double ans = (1 / (ok - or) - 1 / (ok + or)) / 2;
 
        printf("%.8lf\n", ans);
    }
}

你可能感兴趣的:(codeforces)