Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 训练补题
  • Codeforces 刷题计划
  • 概率dp练习
  • dp专题练习
  • 数论练习
  • 搜索专题练习
  • 图论练习
  • 基础字符串练习
  • AtCoder练习
  • 题解们
  • solutions

  • 训练补题

    • 训练补题-个人1
      • Removing Blocks(AtCoder AGC028-B)
      • GTW likes czf(HDU 5598) 数位Dp
      • Little Elephant and Boxes(CodeChef - LEBOXES) MeetInMiddle
      • Cross-stitch(URAL 1035) 题都没看
      • AtCoder AGC028-B
      • HDU 5598
        • 1. 递归版数位dp
        • 2. 循环版
    • 训练补题-个人10
    • 训练补题-个人2
    • 训练补题-个人5
    • 训练补题-个人6
    • 训练补题-个人8
    • 训练补题-个人9
    • 训练补题-牛客训练赛1
    • 训练补题-牛客训练赛10
    • 训练补题-牛客训练赛2
    • 训练补题-牛客训练赛3
    • 训练补题-牛客训练赛6
    • 训练补题-牛客训练赛6
    • 训练补题-牛客训练赛7
    • 训练补题-牛客训练赛8
    • 训练补题-牛客训练赛9
    • 训练补题-杭电多校10
    • 训练补题-杭电多校2
    • 训练补题-杭电多校3
    • 训练补题-杭电多校6
    • 训练补题-杭电多校9
    • NWERC 2018-训练补题11
    • SpbKOSHP 19-训练补题12
    • Gym102576-训练补题13
    • GCPC2018-训练补题14
    • Gym102202-训练补题15

训练补题-个人1

# 个人排位赛1补题记录

NUF9OS.png

# Removing Blocks(AtCoder AGC028-B)

AC代码

两种做法:

  1. 考虑对于每个aja_jaj​,会对答案产生多少贡献。对于i,j(i<j)i,j(i<j)i,j(i<j),若取走iii时产生了aja_jaj​的贡献,说明i,i+1,…,ji,i+1,\dots,ji,i+1,…,j都未被取走,此时,设len=j−i+1len=j-i+1len=j−i+1i+1,i+2,…,ji+1,i+2,\dots,ji+1,i+2,…,j有(len−1)!(len-1)!(len−1)!中排列方法,而小于iii或大于jjj的元素随意插入,共有(len+1)(len+2)…(len−1)len(len+1)(len+2)\dots(len-1)len(len+1)(len+2)…(len−1)len中插法。由乘法原理,可知iii对jjj产生了n!len\frac{n!}{len}lenn!​次贡献

    也就是说,其他元素i≠ji\ne ji=j一共会对jjj产生cntj=∑len=2j(n!len)+∑len=2n−j+1(n!len)cnt_j=\sum\limits_{len=2}^{j}\left(\frac{n!}{len}\right)+\sum\limits_{len=2}^{n-j+1}\left(\frac{n!}{len}\right)cntj​=len=2∑j​(lenn!​)+len=2∑n−j+1​(lenn!​)次贡献,再加上jjj对自身产生的贡献次数n!n!n!。因此jjj一共对答案产生了aj(cntj+n!)a_j(cnt_j+n!)aj​(cntj​+n!)的贡献。

  2. 考虑将代价和变成代价均值乘方案数n!n!n!

    若iii对jjj有贡献,那么区间[i,j][i,j][i,j]内必然先删除iii(与方法1原理相同)。那么jjj产生贡献的期望次数为∑i=1n1∣j−i∣+1\sum\limits_{i=1}^{n}\frac{1}{\mid j-i\mid+1}i=1∑n​∣j−i∣+11​,总的答案为∑j=1naj∑i=1n1∣j−i∣+1\sum\limits_{j=1}^{n}a_j\sum\limits_{i=1}^{n}\frac{1}{\mid j-i\mid+1}j=1∑n​aj​i=1∑n​∣j−i∣+11​

# GTW likes czf(HDU 5598) 数位Dp

AC代码 (PS. 第一次见循环版数位Dp,感觉思路比递归版更清晰)

对一个大小为nnn数集SSS的每一个元素与xxx亦或,得到的新的数集S′S'S′大小仍为nnn,即不同的数与xxx亦或后得到的数不同:假设a⊕x=b⊕xa\oplus x=b\oplus xa⊕x=b⊕x,定有a=ba=ba=b。

那么,该题的答案ans=2(r−l+1)−kans=2(r-l+1)-kans=2(r−l+1)−k,kkk为满足a⊕g=b⊕ta\oplus g=b\oplus ta⊕g=b⊕t的数对(a,b)(a,b)(a,b)的个数。

对式子稍作变换,得到a⊕b=g⊕ta\oplus b=g\oplus ta⊕b=g⊕t,用数位Dp计数。

记f(a′,b′,x)f(a', b',x)f(a′,b′,x)为a∈[0,a′],b∈[0,b′],x=g⊕ta\in[0,a'],b\in[0,b'],x=g\oplus ta∈[0,a′],b∈[0,b′],x=g⊕t时,满足a⊕b=xa\oplus b=xa⊕b=x的对数。

最终答案为2(r−l+1)−(f(r,r,x)−2∗f(l−1,r,x)+f(l−1,l−1,x))2(r-l+1)-(f(r,r,x)-2*f(l-1,r,x)+f(l-1,l-1,x))2(r−l+1)−(f(r,r,x)−2∗f(l−1,r,x)+f(l−1,l−1,x))(容斥)

# Little Elephant and Boxes(CodeChef - LEBOXES) MeetInMiddle

dp[i][j][k]dp[i][j][k]dp[i][j][k]求出前iii个物品买了jjj个,用了kkk个钻石时,使用的最少金币数。

接着把左半边的袋子所有可能性跑出来,按钻石数分组,组内按金币数排序。

右半边袋子同样处理。

枚举左半边钻石数iii,右半边钻石数jjj,购买物品数kkk,双指针扫描两个组即可。

理论AC

# Cross-stitch(URAL 1035) 题都没看

# AC代码

# AtCoder AGC028-B

#include <bits/stdc++.h>
#define ll long long
#define Android ios::sync_with_stdio(false), cin.tie(NULL)

using namespace std;
const int maxn = 1e5 + 100;
const ll mod = 1e9 + 7;

ll n, nfac = 1;
ll a[maxn], presum[maxn];

ll inv(ll base){
    ll t = mod - 2, ret = 1;
    for(; t; t>>=1){
        if(t & 1) ret = ret * base % mod;
        base = base * base % mod;
    }
    return ret;
}

void init(){
    for(int i=2; i<=n; i++) nfac = nfac * i % mod;
    for(int i=2; i<=n; i++){
        presum[i] = (presum[i-1] + nfac * inv(i) % mod) % mod;
    }
}

void solve(){
    cin >> n;
    init();
    ll suma = 0, ans = 0;
    for(int i=1; i<=n; i++) cin >> a[i], suma = (suma + a[i]) % mod;
    for(int i=1; i<=n; i++){
        ans = (ans + (presum[i] + presum[n - i + 1]) % mod * a[i]) % mod;
    }
    ans = (ans + suma * nfac % mod) % mod;
    cout << ans << '\n';
}

signed main(){
    Android;
    solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# HDU 5598

# 1. 递归版数位dp

#include <bits/stdc++.h>
#define ll long long
#define Android ios::sync_with_stdio(false), cin.tie(NULL)

using namespace std;

const ll mod = 1e9 + 7;

int an[64], bn[64], xn[64];
ll dp[64][2][2];
ll calc(int bit, int lim_a, int lim_b){ // 数位dp巨多条件判断
    if(bit == -1) return 1;
    if(~dp[bit][lim_a][lim_b]) return dp[bit][lim_a][lim_b];
    ll & ans = dp[bit][lim_a][lim_b] = 0;
    if(lim_a && lim_b){
        if(xn[bit]){
            if(an[bit]) ans += calc(bit - 1, 1, bn[bit]==0);
            if(bn[bit]) ans += calc(bit - 1, an[bit] == 0, 1);
        }else{
            if(an[bit] && bn[bit]) {
                ans += calc(bit - 1, 0, 0);
                ans += calc(bit - 1, 1, 1);
            }else{
                ans += calc(bit - 1, an[bit]==0, bn[bit]==0);
            }
        }
    }else if(lim_a == 0 && lim_b == 0){
        ans = 2 * calc(bit - 1, 0, 0);
    }else{
        ans += calc(bit - 1, lim_a, lim_b);
        if(lim_a && an[bit]) ans += calc(bit - 1, 0, 0);
        if(lim_b && bn[bit]) ans += calc(bit - 1, 0, 0);
    }
    return ans %= mod;
}

void set_val(ll a, ll b, ll x){
    memset(dp, -1, sizeof dp);
    for(int i=62; i>=0; i--){
        an[i] = a >> i & 1;
        bn[i] = b >> i & 1;
        xn[i] = x >> i & 1;
    }
}

void solve(){
    ll l, r, g, t;
    cin >> l >> r >> g >> t;
    set_val(r, r, g ^ t);
    ll ans = calc(63, 1, 1);
    set_val(r, l - 1, g ^ t);
    ll tmp = calc(63, 1, 1) * 2;
    set_val(l - 1, l - 1, g ^ t);
    ans = ((ans - tmp + mod) % mod + calc(63, 1, 1)) % mod;
    cout << (2 * (r - l + 1) - ans + mod) % mod << '\n';
}

signed main(){
    Android;
    int t; cin >> t;
    while(t--)
        solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 2. 循环版

#include <bits/stdc++.h>
#define ll long long
#define Android ios::sync_with_stdio(false), cin.tie(NULL)

using namespace std;
const ll mod = 1e9 + 7;

int an[64], bn[64], xn[64];
ll dp[64][2][2];

void add(ll & val, ll delta){val = (val + delta) % mod;}

ll calc(ll a, ll b, ll x){
    memset(dp, 0, sizeof dp);
    for(int i=62; i>=0; i--){
        an[i] = a >> i & 1;
        bn[i] = b >> i & 1;
        xn[i] = x >> i & 1;
    }
    dp[63][1][1] = 1;
    for(int i=62; i>=0; i--){
        if(an[i] ^ bn[i] == xn[i]) add(dp[i][1][1], dp[i+1][1][1]);
        add(dp[i][1][0], dp[i+1][1][0]);
        if(xn[i] == an[i] && bn[i]) add(dp[i][1][0], dp[i+1][1][1]);
        add(dp[i][0][1], dp[i+1][0][1]);
        if(xn[i] == bn[i] && an[i]) add(dp[i][0][1], dp[i+1][1][1]);
        // dp[i][0][0]可以从[0][0], [0][1], [1][0], [1][1]转移过来
        // 在if()里填入对应条件即可
        add(dp[i][0][0], 2 * dp[i+1][0][0]);
        if(an[i]) add(dp[i][0][0], dp[i+1][1][0]);
        if(bn[i]) add(dp[i][0][0], dp[i+1][0][1]);
        if(an[i] && bn[i] && xn[i]==0) add(dp[i][0][0], dp[i+1][1][1]);
    }
    return (dp[0][0][0] + dp[0][1][0] + dp[0][0][1] + dp[0][1][1]) % mod;
}


void solve(){
    ll l, r, g, t, ans;
    cin >> l >> r >> g >> t;
    ans = (calc(r, r, g ^ t) + calc(l - 1, l - 1, g ^ t) - 2 * calc(r, l - 1, g ^ t) % mod + mod) % mod;
    cout << (2 * (r - l + 1) - ans + mod) % mod << '\n';
}

signed main(){
    Android;
    int t; cin >> t;
    while(t--) solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
上次更新: 2021/02/24, 03:37:30
Codeforces 578D
训练补题-个人10

← Codeforces 578D 训练补题-个人10→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式