Topcoder Srm 654 DIV1

Srm 654 250pt SquareScores


题意:定义一个字符串的分数为有多少个子串,且子串中每个字符都相差,比如aaaba,得分是8。给出一个字符串,由小写英文字母和'?'组成,对于'?',给出它填上每个英文字母的概率。问给出字符串的期望分数

思路:DP,设dp[i][j][k]为处理到字符串的第i位,有连续j位以k这个字符为结尾的概率。

                      如果第i + 1位的字符不是问号,如果这个字符为k,则dp[i + 1][j + 1][k] += dp[i][j][k];

                                                                                                               否则 dp[i + 1][1][s[i + 1]] += dp[i][j][k];

                       如果第i + 1位的字符是问号,枚举这个问号填哪个字符,然后类似上面的转移

                      对于每次求出dp[i][j][k]后,ans += dp[i][j][k] * j,表示新增加的分数的期望

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
double dp[2][1010][26];
struct SquareScores {
    double calcexpectation(vector <int> p, string s) {
        double pt[26];
        int n = s.size();
        int m = p.size();
        double ans = 1;
        for(int i = 0; i < m; i ++) pt[i] = 1.0 * p[i] / 100;
        if(s[0] >= 'a' && s[0] <= 'z') dp[1][1][s[0] - 'a'] = 1;
        else {
            for(int j = 0; j < m; j ++)
                dp[1][1][j] = pt[j];
        }
        for(int i = 2; i <= n; i ++) {
            int flag = i & 1;
            memset(dp[flag],0,sizeof(dp[flag]));
            if(s[i - 1] == '?') {
                for(int j = 1; j <= i; j ++)
                    for(int k = 0; k < m; k ++)
                        if(j == 1) {
                            double sum = 1;
                            for(int jj = 1; jj <= i; jj ++)
                                sum -= dp[flag ^ 1][jj][k];
                            dp[flag][j][k] = sum * pt[k];
                        }
                        else dp[flag][j][k] += pt[k] * dp[flag ^ 1][j - 1][k];
            }
            else {
                int idx = s[i - 1] - 'a';
                for(int len = 1; len <= i; len ++) {
                    if(len == 1) {
                        for(int j = 1; j <= i; j ++)
                            for(int k = 0; k < m; k ++)
                                if(k != idx) dp[flag][len][idx] += dp[flag ^ 1][j][k];
                    }
                    else dp[flag][len][idx] += dp[flag ^ 1][len - 1][idx];
                }
            }
            for(int j = 1; j <= i; j ++)
                for(int k = 0; k < m; k ++)
                    ans += dp[flag][j][k] * j;
        }
        return ans;
    }
};

Srm 654 450pt SuccessiveSubtraction2

题意:给出一个序列a,长度为N <= 2000,有<=2000个修改,每次修改是将a[i]的值变为x,然后输出这个数组经过操作后的最大值。 所以操作值的是,初始数组的价值是a[1] - a[2] - a[3] - ... - a[N],你可以插入不超过2对括号,使得这个数组的价值最大。比如1 - 3 - 5 - 7 - 10,可以变成 1 - (3 + 5 + 7 + 10)。


思路: 对于每个修改,因为数据范围<=2000,可以直接O(N)求出数组的价值。观察可以看出,对于一个数,如果其前面的左括号 - 右括号的次数为奇数,则其变号为+,否则符号不变。因为dp[i][j][k]表示前i项,有j个左括号,k个右括号即可。(j <= 2,k <= 2,k <= j) 

#include <bits/stdc++.h>
using namespace std;

const int N = 2010;
int dp[N][3][3];

void update(int &x,int y)
{
    x = max(x,y);
}

struct SuccessiveSubtraction2 {
    vector <int> calc(vector <int> a, vector <int> p, vector <int> v) {
        vector<int> ret;
        for(int pos = 0; pos < p.size(); pos ++) {
            a[p[pos]] = v[pos];
            int n = a.size();
            for(int i = 0; i <= n; i ++)
                for(int j = 0; j < 3; j ++)
                    for(int k = 0; k < 3; k ++)
                        dp[i][j][k] = -1000000;
            dp[1][0][0] = a[0];
            for(int i = 1; i < n; i ++)
                for(int j = 0; j < 3; j ++)
                    for(int k = 0; k <= j; k ++) {
                        if(dp[i][j][k] == -1000000) continue;
                        int sign = (j - k) % 2 ? (-1) : 1;
                        update(dp[i + 1][j][k],dp[i][j][k] - sign * a[i]);
                        if(j > k) update(dp[i + 1][j][k + 1],dp[i][j][k] - sign * a[i]);
                        if(j > k + 1) update(dp[i + 1][j][k + 2],dp[i][j][k] - sign * a[i]);
                        if(j + 1 < 3) update(dp[i + 1][j + 1][k],dp[i][j][k] - sign * a[i]);
                        if(j + 2 < 3) update(dp[i + 1][j + 2][k],dp[i][j][k] - sign * a[i]);
                    }
            int ans = -10000000;
            for(int j = 0; j < 3; j ++)
                for(int k = 0; k < 3; k ++)
                    if(j == k) ans = max(ans,dp[n][j][k]);
            ret.push_back(ans);
        }
        return ret;
    }
};


Srm 654 850pt TwoEntrances


题意,给出N <= 3000个点的树,给出两个点s1,s2(s1 != s2),s1,s2是两个出入口,你可以从s1或s2运送货物到树上的每个点,你能运送货物到点x,当且仅当存在一条路径s1->x,或者s2->x,满足路径上每个点都是没有货物的。 问存在多少种合法的运货顺序 % (10^9 + 7).


思路:如果只有一个出入口s1,合法序列的方法数是很容易算的,s1比如安是最后的点。同理,用fu]表示以u为根的子树合法的运货顺序。则根节点是最后一个运货的。f假设v是f[u]的儿子节点,则f[u] *= f[v],且因为各儿子运货的顺序是随意的,所以f[u] *= combination(u的子树中还有多少位置没被用,v的子树大小)

            两个出入口s1,s2的情况。假设s1->s2的路径为s1->a1->a2->..->an->s2。 首先两个端点s1,s2必然有一个是最后运货的,假设是s1,则a2->a2->..->an->s2是类似的问题。因此可以用dp[x][y]表示(x,y)这条路经上的两个端点,只能转移到dp[x + 1][y]和dp[x][y - 1],同时,对于路径上的一个点ai,运货必然是经过它的,所以可以看成是以它为根,一个点的运货问题,即上一段。所以要乘上 f[ai] * combination(空余位置,ai的后代数目)


#include <bits/stdc++.h>
using namespace std;

const int N = 3010;
const int mod = 1000000007;
int c[N][N],dp[N][N],pre[N],visit[N],sz[N],f[N];
vector<int> edges[N],path;
void init()
{
    for(int i = 0; i < N; i ++) {
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; j ++) {
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
        }
    }
}

void dfs(int u,int fa)
{
    pre[u] = fa;
    for(int i = 0; i < edges[u].size(); i ++)
        if(edges[u][i] != fa) dfs(edges[u][i],u);
}

void get_size(int u,int pre)
{
    sz[u] = 1;
    for(int i = 0; i < edges[u].size(); i ++)
        if(edges[u][i] == pre || visit[edges[u][i]]) continue;
        else {
            get_size(edges[u][i],u);
            sz[u] += sz[edges[u][i]];
        }
}

void dfs1(int u,int pre)
{
    f[u] = 1;
    int tot = sz[u] - 1;
    for(int i = 0; i < edges[u].size(); i ++) {
        int x = edges[u][i];
        if(x == pre || visit[x]) continue;
        dfs1(x,u);
        f[u] = 1LL * f[u] * c[tot][sz[x]] % mod * f[x] % mod;
        tot -= sz[x];
    }
}

int dfs2(int x,int y,int tot)
{
    if(dp[x][y] != -1) return dp[x][y];
    if(x == y) return dp[x][y] = f[path[x]];
    int tmp = dfs2(x + 1,y,tot - sz[path[x]]);
    dp[x][y] = 1LL * tmp * c[tot - 1][sz[path[x]] - 1] % mod * f[path[x]] % mod;
    tmp = dfs2(x,y - 1,tot - sz[path[y]]);
    dp[x][y] = (dp[x][y] + 1LL * tmp * c[tot - 1][sz[path[y]] - 1] % mod * f[path[y]]) % mod;
    return dp[x][y];
}

struct TwoEntrances {
    int count(vector <int> a, vector <int> b, int s1, int s2) {
        int n = a.size() + 1;
        for(int i = 0; i < n - 1; i ++) {
            edges[a[i]].push_back(b[i]);
            edges[b[i]].push_back(a[i]);
        }
        dfs(s1,-1);
        init();
        int x = s2;
        while(x != -1) {
            visit[x] = 1;
            path.push_back(x);
            x = pre[x];
        }
        reverse(path.begin(),path.end());
        for(int i = 0; i < path.size(); i ++) get_size(path[i],-1);
        for(int i = 0; i < path.size(); i ++) dfs1(path[i],-1);
        memset(dp,-1,sizeof(dp));
        return dfs2(0,path.size() - 1,n);
    }
};




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值