zoukankan      html  css  js  c++  java
  • 训练赛Day6-The 36th ACMICPC Asia Regional Beijing Site

    训练赛Day6-The 36th ACM/ICPC Asia Regional Beijing Site

    B - Eliminate Witches!

    题目大意

    【样例解释】

    给你一个字符串:

    walpurgis(charlotte(patricia,gertrud),elly,gisela) 

    如下图:

    img

    现在要你输出该树节点个数以及先序遍历,和遍历中的访问边的顺序。例如该样例输出如下:

    6 
    walpurgis 
    charlotte 
    patricia 
    gertrud 
    elly 
    gisela 
    1 2 
    2 3 
    3 2 
    2 4 
    4 2 
    2 1 
    1 5 
    5 1 
    1 6 
    6 1 

    数据范围

    多组输入T <= 20,每个字符串不超过50000个节点,且每个节点对应的字符串长度不超过10,所有输入字符串长度不超过1000000。

    解题思路

    暴力模拟,首先可以看出该树的先序遍历就是字符串的先后出现顺序,并且还可以发现树的节点个数就是字符串中’(‘的个数加’,’的个数在加一,也就是节点个数 = cnt[‘(‘] + cnt[‘,’] + 1。之后再暴力Dfs模拟先序遍历顺序以及回溯过程即可。

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    using namespace std;
    const int maxn = 1000000;
    int T, len, l, now, tot;
    char s[maxn + 5];
    char c[15];
    vector<pair<int, int> >ans;
    void solve() {
        int tot = -1;
        for(int i = 0; i < len; i++){
            if(s[i] >= 'a' && s[i] <= 'z')c[++tot] = s[i] ;
            else if(s[i] == ',') {
                if(tot >= 0) {
                    c[++tot] = '';
                    printf("%s
    ", c);
                    tot = -1;
                }
            }
            else if(s[i] == '(') { 
                c[++tot] = '';
                printf("%s
    ", c);
                tot = -1;
            }
            else if(s[i] == ')') {
                if(tot >= 0) {
                    c[++tot] = '';
                    printf("%s
    ", c);
                    tot = -1;
                }
            }
        }
        if(tot >= 0) {
            c[++tot] = '';
            printf("%s
    ", c);
        }
    }
    void Dfs(int fa) {
        while(l < len) {
            if(s[l] >= 'a' && s[l] <= 'z')++tot, l++;
            else if(s[l] == ',') {
                if(tot >= 0) {
                    tot = -1;
                    now++;
                    ans.push_back(make_pair(fa, now));
                    ans.push_back(make_pair(now, fa));
                }
                l++;
            }
            else if(s[l] == '(') { 
                tot = -1;
                now++;
                if(fa != 0)ans.push_back(make_pair(fa, now));
                l++;
                int tmp = now;
                Dfs(now);
                if(fa != 0)ans.push_back(make_pair(tmp, fa)); 
            }
            else if(s[l] == ')') {
                if(tot >= 0) {
                    now++;
                    ans.push_back(make_pair(fa, now));
                    ans.push_back(make_pair(now, fa));
                    tot = -1;
                }
                l++;
                return ;
            }
        }
    
    }
    int main() {
        scanf("%d", &T);
        while(T--) {
            ans.clear(); now = 0, l = 0, tot = -1;
            scanf("%s", s);
            len = strlen(s);
            int p = 0, i = 0;
            while(i < len) {
                if(s[i] == '(' || s[i] == ',')p++;
                i++;
            }
            printf("%d
    ", p + 1);
            solve();
            Dfs(0);
            for(int i = 0; i < ans.size(); i++)
                printf("%d %d
    ", ans[i].first, ans[i].second);
            printf("
    ");
        }
        return 0;
    }

    D - FXTZ II

    题目大意

    A,B两个人在玩一个游戏,即有n个球,球的标号是从1~n的,并且每个球有一个攻击力,攻击力的大小个球的标号有关,标号为i的球的攻击力等于2i1。现在游戏规则是这样的,A每次随机出一个球,并且每个球只能被随机到一次,且这个球会有50%的概率攻击A,同样也有50%的概率攻击B。A,B初始血量都是2n。每一轮过后,如果A的血量低于B的血量那么A就输了,否则当所有球都用完之后A还没有输的话,那么A就赢了,现在问你A赢的概率。要求以分数最简式的形式输出答案。

    数据范围

    多组输入T<=30,球的个数n<=500。

    解题思路

    首先我们可以知道2i1=20+21++2i1。所以,假如B得了一个标号最大的球,那么不管之后怎么取A一定是赢的,而当B得了一个i号球,且i不等于n,那么之后A要赢的话之后一轮取到比i大的球都必须给B,也就是B取到球的标号是递增的,而我们可以知道n个球一共有n!种取球的顺序,那么我们对于每一个排列顺序都可以知道A赢的概率,求出所有的排列的概率在求和即可求出A赢的概率。但是n有500那么大,如果照这个方法暴力显然不能写,但是通过打表打得n从1~10的几项发现,在未最简化的情况下,每一项的分母都是n!2n,而分子则呈现第i项的分子是第i-1项分子的2 * i - 1倍。看到分母应该就得想到显然得大整数写了,Java的大整数就行。

    AC代码

    import java.util.*;
    import java.math.*;
    
    public class Main {
        static BigInteger zo = new BigInteger("0");
        public static BigInteger Gcd(BigInteger x, BigInteger y) {
            if(x.remainder(y).compareTo(zo) == 0) return y;
            return Gcd(y, x.remainder(y));
        }
        public static void main(String []args) {
            Scanner cin = new Scanner(System.in);
            BigInteger [] fac = new BigInteger[505];
            BigInteger [] f = new BigInteger[505];
            BigInteger [] Pow = new BigInteger[505];
            fac[0] =new BigInteger("1");
            f[0] = new BigInteger("1");
            Pow[0] = new BigInteger("1");
            for (int i = 1; i <= 500; i++) {
                BigInteger val = BigInteger.valueOf(i);
                fac[i] = fac[i - 1].multiply(val);
                BigInteger v = BigInteger.valueOf(2 * i - 1);
                f[i] = f[i - 1].multiply(v);
                BigInteger w = new BigInteger("2");
                Pow[i] = Pow[i - 1].multiply(w);
            }
            int T = cin.nextInt();
            for (int cas = 1; cas <= T; cas++) {
                int n = cin.nextInt();
                BigInteger gcd = Gcd(f[n], fac[n].multiply(Pow[n]));
                System.out.println(f[n].divide(gcd).toString() + "/" + fac[n].multiply(Pow[n]).divide(gcd).toString());
            }
        }
    }

    懒得写了,放了队友代码。。。。


    F - Machine scheduling

    题目大意

    n个数取r个分成小于等于m组的方案数,并且取出的r个数要求两两之间的差值必须大于等于k。

    数据范围

    1n,r,k,m1000,要求答案模109+7

    解题思路

    首先将此题分成两部分分开求解,第一部分是n个数取r个满足要求的有多少个,第二部分是r个数分成m的方案数。因为两个问题相互独立,所以最后答案就是第一部分的方案数乘上第二部分的方案数。

    首先看第二部分,是一个斯特林数,也可以看做是一个DP问题,S[i][j]代表前i个数分成j组的方案数,那么S[i][j]可以由S[i-1][j-1]转移过来,因为可以将第i个数单独作为第j组;S[i][j]也可以由S[i-1][j]转移过来,第i个数可以放在j组中的任意一组,所以总转移状态为S[i][j]=S[i1][j1]+S[i1][j]j。所以第二部分总的方案数就是j=0mS[n][j]

    然后看第一部分,n个数取r个即有d=n((r1)k+1)个空闲的数,因为这样保证了满足任意两个数差值大于等于k的条件,然后可以看做成r+d个数取r个数的方案个数。

    最后两部分相乘取模即可。

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int maxn = 1010;
    const int Pt = 1e9 + 7;
    int n, r, k, m;
    LL c[maxn + 5][maxn + 5];
    LL S[maxn + 5][maxn + 5];
    void Init() {
        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]) % Pt;
        for(int i = 0; i <= maxn; i++)S[i][0] = 0LL, S[i][i] = 1LL; S[0][0] = 1LL;
        for(int i = 1; i <= maxn; i++)
            for(int j = 1; j <= i; j++)
                S[i][j] = (S[i - 1][j - 1] + S[i - 1][j] * j % Pt) % Pt;
    }
    int main() {
        Init();
        while(scanf("%d%d%d%d", &n, &r, &k, &m) != EOF) {
            int d = n - ((r - 1) * k + 1);
            if(d < 0)printf("0
    ");
            else {
                d += r; 
                LL res = 0LL;
                for(int i = 0; i <= m; i++) res = (res + c[d][r] * S[r][i] % Pt) % Pt;
                printf("%lld
    ", res);
            }
        }
        return 0;
    }

    G - Panda

    题目大意

    给你一个长度为n的且只包含字符’b’和’w’的字符串,然后m个操作,操作0代表查询区间[l,r]字符串”wbw”的个数(可以重叠),操作1代表修改第k个字符为ch。

    数据范围

    多组输入T<=100,n<=50000,m<=10000。

    解题思路

    可以发现每做一次修改只会影响到3个位置的值,因为查询较多,可以用树状数组维护前i个字符有多少个”wbw”串,并记下以第i个结尾是否有”wbw”,那么修改k位置的话,只需要先将k,k+1,k+2位置的答案先减掉,之直接修改字符串并判断k,k+1,k+2位置有无”wbw”串,有则更新答案。

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn = 50000;
    char s[maxn + 5];
    int c[maxn + 5];
    int vis[maxn + 5];
    int n, m;
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, int y) {
        while(x <= maxn) {
            c[x] += y;
            x += lowbit(x);
        }
    }
    int sum(int x) {
        int res = 0;
        while(x > 0) {
            res += c[x];
            x -= lowbit(x);
        }
        return res;
    }
    int main() {
        int T, cas = 0; scanf("%d", &T);
        while(T--) {
            memset(vis, 0, sizeof(vis));
            memset(c, 0, sizeof(c));
            scanf("%d%d", &n, &m);
            scanf("%s", s + 1);
            for(int i = 1; i <= n - 2; i++) {
                if(s[i] == 'w' && s[i + 1] == 'b' && s[i + 2] == 'w') {
                    vis[i + 2] = 1;
                    add(i + 2, 1);
                }
            }
            printf("Case %d:
    ", ++cas);
            while(m--) {
                int op;
                scanf("%d", &op);
                if(op == 0) {
                    int l, r;
                    scanf("%d%d", &l, &r); l++, r++;
                    printf("%d
    ", max(0, sum(r) - sum(l + 1)));
                }
                else {
                    int l; char ch;
                    scanf("%d %c", &l, &ch); l++;
                    s[l] = ch;
                    for(int i = l; i <= min(l + 2, n); i++) {
                        add(i, -vis[i]);
                        vis[i] = 0;
                    }
                    for(int i = max(1, l - 2); i <= l; i++) {
                        if(s[i] == 'w' && s[i + 1] == 'b' && s[i + 2] == 'w') {
                            add(i + 2, 1);
                            vis[i + 2] = 1;
                        }
                    }
                }
            }
        }
    }

    J-Tourism Planning

    题目大意

    有n个人,m个景点,每个景点都需要门票,每去一个景点就需要花费对应的兴趣值,然后每个人去景点也会获得对应的兴趣值,并且特别的如果多个人在一个景点将会获得额外的兴趣值。现在要你求最大兴趣值和。如果不大于0则输出”STAY HOME”。

    数据范围

    1N10,1M10,分别代表N个人和M个景点,1Pi1000,代表一个人去第i个地方需要花费的兴趣值,1iN,1jM,1Vij1000,代表第i个人去第j个景点获得的兴趣值,1iN,1jN,1Bij1000,代表i,j两个人在同一景点会产生的兴趣值。

    解题思路

    dp[i][s]代表前i个景点在访问为s的状态下的最大兴趣值。那么它可以由第i-1的景点的状态转移过来,但是因为人数是不递增的,那么dp[i][s]=max(dp[i1][j]+c[i][s]),其中sj(1<<n)1,c[i][s]代表第i个景点在s状态下的兴趣值,而c[i][s]的话,对每个状态找出哪些人访问了该景点,以及多个人之间的额外兴趣值。

    具体见代码:

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int INF = 1000000;
    const int maxn = 2000;
    int dp[15][maxn + 5], c[15][maxn + 5];
    int p[15], v[15][15], b[15][15];
    int n, m;
    void Init() {
        for(int i = 1; i <= m; i++) {
            for(int s = 0; s < (1 << n); s++) {
                c[i][s] = 0;
                for(int j = 1; j <= n; j++) {
                    if((1 << (j - 1)) & s) {
                        c[i][s] += (v[j][i] - p[i]);
                        for(int k = 1; k < j; k++) {
                            if((1 << (k - 1) & s))c[i][s] += b[j][k];
                        }
                    }
                }
            }
        }
    }
    void solve() {
        int Max = -INF;
        for(int i = 1; i <= m; i++)
            for(int s = 0; s < (1 << n); s++)dp[i][s] = -INF;
        for(int i = 1; i <= m; i++) {
            for(int s = 0; s < (1 << n); s++) {
                for(int j = s; j < (1 << n); j++) {
                    if((s & j) == s) {
                        dp[i][s] = max(dp[i][s], dp[i - 1][j] + c[i][s]);
                    }
                }
                if(i == m)Max = max(Max, dp[m][s]);
            }
        }
        if(Max <= 0)printf("STAY HOME
    ");
        else printf("%d
    ", Max);
    } 
    int main() {
        while(~scanf("%d%d", &n, &m)) {
            if(n == 0 && m == 0)break;
            for(int i = 1; i <= m; i++)scanf("%d", &p[i]);
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= m; j++)
                    scanf("%d", &v[i][j]);
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    scanf("%d", &b[i][j]);
            Init();
            solve();
        }
        return 0;
    }
  • 相关阅读:
    JQuery Ajax动态生成表格
    简单模拟JQuery框架
    事务和锁学习
    Js面向对象和数据类型内存分配
    Spring.Net学习 控制反转(IoC)和面向切面编程(AOP)
    html <pre>标签
    裸眼3D
    sysobjects syscolumns和SysTypes笔记
    JQuery EasyUi练习Demo(带源码)
    Oracle 游标使用大全
  • 原文地址:https://www.cnblogs.com/TRDD/p/9813512.html
Copyright © 2011-2022 走看看