AtCoder Beginner Contest 382 Solution

题目大意

给定一个长度为N的字符串,有很多.@,一共有D天,每天会使一个@变成.,问D天之后有几个.

解题思路

数一下有几个.,答案会加D个.

代码

void solve()
{
    int n, d;
    string s;
    cin >> n >> d >> s;
    cout<<count(s.begin(),s.end(),'.')+d;
}

题目大意

题意和第一题差不多,但是每天会让最右边的@变成.,问D天之后的字符串长什么样。

解题思路

按照题意模拟即可。

代码

void solve()
{
    int n, d;
    string s;
    cin >> n >> d >> s;
    int cnt = 0;
    for(int i=(int)s.size()-1;i>=0;i--){
        if (s[i] == '@') {
            cnt++;
            s[i] = '.';
            if (cnt == d) break;
        }
    }
    cout << s;
}

C - Kaiten Sushi (abc382 C)

题目大意

题目意思很复杂,可以自己阅读一下,我这里简化题意。

给定一个长度为N的数组A,并且有M次查询B。
对于第i个查询Bi问A数组里从左往右第一个小于等于Bi的数的下标是多少

解题思路

题目是离线的,那么B数组的顺序无关紧要,把他压进一个优先队列里,然后从左向右遍历A数组,每次把所有大于等于Ai的数都弹出队列并且标记答案就可以了。

代码

#define rep(i,n) for(int i=0;i<n;i++)
priority_queue<pair<int,int> > q;
int a[200005],b[200005];
int ans[200005];
void solve()
{
    int n, m;
    cin >> n >> m;
    rep(i,n) cin>>a[i];
    rep(i,m) cin>>b[i];

    rep(i, m) q.push(make_pair(b[i], i));
    rep(i, n) {
        while (!q.empty() && q.top().first >= a[i]) {
            ans[q.top().second] = i + 1;
            q.pop();
        }
    }
    rep(i,m) cout<<ans[i]<<endl;
}

D - Keep Distance (abc382 D)

题目大意

给定N和M,按字典序输出所有满足以下条件的数组:

  • \(1 \le A_i\)
  • 对于i大于等于2,\(A_{i-1}+10 \le A_i\)
  • \(A_N \le M\)

解题思路

这题关键点在于\(10N-9 \le M \le 10N\),那么说明中间的差其实不会超过20,否则最后一个数一定会超过M。

然后暴力即可。

代码

vector<vector<int> > ans;
int n, m;
vector<int> tmp;
void dfs(int x) {
    if (x == n) {
        ans.pb(tmp);
        return;
    }
    if (tmp[x - 1] + (n - x) * 10 > m) return;
    for (int i = 10;i <= 20;i++) {
        tmp[x] = tmp[x - 1] + i;
        if (tmp[x] > m) return;
        dfs(x + 1);
    }
}

void solve()
{
    cin >> n >> m;
    tmp.resize(n);
    for (int i = 1;i <= 10;i++) {
        tmp[0] = i;
        dfs(1);
    }
    cout << ans.size() << endl;
    //以下为auto输出ans
    for(auto v, ans) {
        for(auto i, v) cout << i << " ";
        cout << endl;
    }
}

E - Expansion Packs (abc382 E)

题目大意

一个卡牌包里有n张卡牌,第i张卡牌是稀有牌的概率是pi。

现在将一包一包的开牌,直到开出X张稀有牌为止,问开的卡牌包数的期望是多少

解题思路

看通过人数可以发现期望放在E题不是很合适。

首先求出dp[i][j]为一包中拆前i张牌,有j张是稀有牌的概率:

第i张不是稀有牌,转移:dp[i][j] = dp[i - 1][j] * (1 - p[i])
第i张是稀有牌,转移: dp[i][j] += dp[i - 1][j - 1] * p[i]

然后这个数组我们其实只用得到dp[n][i],其实也就是一整包中开出i张稀有牌的概率,所以dp其实可以滚动数组,但是这题n^2是可以存的下的。

然后设e[i]为开出i张稀有牌的期望包数(即答案)。

那么可以考虑新开出的”一包“中有几张稀有牌,我们先假设这一包不可能没有稀有牌,那么可以很简单的得到转移:e[i]=sigma(e[i-j]*dp[n][j])+1,就是开出i-j张稀有牌的期望*这一包开出j张稀有牌的概率,最后加上1表示我们新开了一包。

但是其实会开不出稀有牌,那么我们要先计算一下期望多少包一定开出稀有牌,是个简单的0-1概型:1/(1-dp[n][0]),所以式子调整成e[i]=sigma(e[i-j]*dp[n][j])+1/(1-dp[n][0]),但是此时还不对,因为内部的dp[n][j]是包含了开不出稀有牌的概率,但我们已经将开不出稀有牌的期望放在外面计算了:
所以最后的式子应该是e[i]=sigma(e[i-j]*(dp[n][j]/(1-dp[n][0])))+1/(1-dp[n][0])

好在给的样例还是很有代表性的。

代码

double dp[5005][5005];
double e[5005];
void solve() {
    int n, x;
    cin >> n >> x;
    vector<double> p(n);
    for(int i=0;i<n;i++)
        cin >> p[i];
        p[i] /= 100.0;
    }
    dp[0][0] = 1.0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= n; ++j) {
            dp[i][j] = dp[i - 1][j] * (1 - p[i - 1]);
            if (j > 0) {
                dp[i][j] += dp[i - 1][j - 1] * p[i - 1];
            }
        }
    }
    for (int i = 1;i <= x;i++) {
        for (int j = 1;j <= i;j++) {
            e[i] += (e[i - j]) * (dp[n][j] / (1.0 - dp[n][0]));
        }
        e[i] += (1.0 / (1.0 - dp[n][0]));
    }
    cout << fixed << setprecision(10) << e[x];
}

posted @ 2024-11-30 23:30  ~清渠~  阅读(163)  评论(0编辑  收藏  举报