CF1657 --- Educational Codeforces Round 125 (Rated for Div. 2)

A. Integer Moves

Tutorial

如果在原点 , 需要 0 步

如果距离是整数 , 需要 1 步

注意到沿对角线和沿直线移动只需要一步

所以最多只需要 2 步

Code

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

std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;

#define ls (loc<<1)
#define rs ((loc<<1)|1)

const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int x, y;

void solve(cint T) {
    cin >> x >> y;
    if(x == 0 && y == 0) { cout << 0 << '\n'; return; }
    int r = x*x + y*y;
    if(int(sqrt(r))*int(sqrt(r)) == r) { cout << 1 << '\n'; return; }
    cout << 2 << '\n';
    return;
}

int main() {
    //freopen("1.in", "r", stdin);
    //cout.flags(ios::fixed); cout.precision(8);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T_=1;
    std::cin >> T_;
    for(int _T=1; _T<=T_; _T++)
        solve(_T);
    return 0;
}

B. XY Sequence

Tutorial

注意到 , 对于任意一种方案 , 将 + x +x +x 向前移动 (如果可以) 会使答案更优

这指明了一种贪心策略 , 能加就加否则减

Code

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

std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;

#define ls (loc<<1)
#define rs ((loc<<1)|1)

const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

ll n, b, x, y;


void solve(cint T) {
    ll ans = -inf_ll;
    ll sum = 0;
    ll now = 0;
    cin >> n >> b >> x >> y;
    for(int i=1; i<=n; i++) {
        if(now + x <= b) { now += x; }
        else { now -= y; }
        sum += now;
    }
    cout << sum << '\n';
}

int main() {
    freopen("1.in", "r", stdin);
    //cout.flags(ios::fixed); cout.precision(8);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T_=1;
    std::cin >> T_;
    for(int _T=1; _T<=T_; _T++)
        solve(_T);
    return 0;
}

C. Bracket Sequence Deletion

Tutorial

注意题目中要的是最短的满足条件的前缀

所以如果开头是 ( , 那么无论下一个是什么都满足条件

如果开头是 ) , 那么不可能形成合法的括号序 , 必须要形成回文串 , 所以结尾必是 )

由于是最短的 , 所以只需要找到除开头外第一个 ) 的位置就可以了

模拟即可

Code

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

std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;

#define ls (loc<<1)
#define rs ((loc<<1)|1)

const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int n;
char s[500500];


void solve(cint T) {
    cin >> n;
    cin >> s;
    int i = 0;
    int sum = 0;
    while(i < n-1) {
        if(s[i] == '(') { sum++; i += 2; }
        else {
            bool flag = false;
            for(int j=i+1; j<n; j++) {
                if(s[j] == ')') { flag = true; sum++; i=j+1; break; }
            }
            if(!flag) { break; }
        }
    }
    cout << sum << ' ' << max(0, n-i) << '\n';
}

int main() {
    freopen("1.in", "r", stdin);
    //cout.flags(ios::fixed); cout.precision(8);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T_=1;
    std::cin >> T_;
    for(int _T=1; _T<=T_; _T++)
        solve(_T);
    return 0;
}

D. For Gamers. By Gamers.

Tutorial

首先先把式子写出来 , 设 s i s_i si 代表选第 i 种时选了 s i s_i si

可以得到当玩家选了第 i 种人时可以获得胜利时满足的不等式

h i D j > H j s i d i \cfrac{h_i}{D_j} > \cfrac{H_j}{s_id_i} Djhi>sidiHj

s i h i d i > D j H j s_ih_id_i > D_j H_j sihidi>DjHj

可以发现 i 和 j 分离了 , 所以可以分别处理 , 记 a i = h i d i a_i=h_id_i ai=hidi , b j = D j H j b_j = D_jH_j bj=DjHj

显然 , 对于 s i s_i si 相同的单位 , 肯定要选择 a i a_i ai 更大的 , 而 C C C 的范围只有 1e6

立即可以得到一种方法 , 枚举每一个 k ∈ C k \in C kC , 计算 c i = k c_i = k ci=k 的单位在买了 1, 2, …, t 次下可以满足的范围

最后做一个前缀最大值 , 对于询问二分查找

复杂度是调和级数

Code

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

std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;

#define ls (loc<<1)
#define rs ((loc<<1)|1)

const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int n, m, C;
ll c[300300], h[300300], d[300300];
ll D[300300], H[300300];
ll mx[1000100];
ll val[1000100];

void solve(cint T) {
    cin >> n >> C;
    for(int i=1; i<=n; i++) { cin >> c[i] >> d[i] >> h[i]; }
    for(int i=1; i<=n; i++) { mx[c[i]] = max(mx[c[i]], d[i]*h[i]); }
    for(int i=1; i<=C; i++) {
        if(mx[i]) {
            for(int j=1; i*j<=C; j++) {
                val[i*j] = max(val[i*j], mx[i]*j);
            }
        }
    }
    for(int i=1; i<=C; i++) { val[i] = max(val[i], val[i-1]); }
    cin >> m;
    ll nd, nh, now;
    for(int i=1; i<=m; i++) {
        cin >> nd >> nh;
        now = nd * nh;
        int r = upper_bound(val+1, val+1+C, now) - val;
        if(r <= C) { cout << r << '\n'; }
        else { cout << -1 << '\n'; }
    }
}

int main() {
    freopen("1.in", "r", stdin);
    //cout.flags(ios::fixed); cout.precision(8);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T_=1;
    //std::cin >> T_;
    for(int _T=1; _T<=T_; _T++)
        solve(_T);
    return 0;
}

E. Star MST

Tutorial

首先可以发现依照题目所说 , 所有连向 1 的边需要形成一棵最小生成树 , 记为 (*)

那么考虑任取两个点 i, j (不等于 1) , 1ij(和连接它们的三条边)构成一个三角形 , 由于 (*) 的存在 , 可以得到 d 1 i ≤ d i j d_{1i} \leq d_{ij} d1idij 并且 d 1 j ≤ d i j d_{1j} \leq d_{ij} d1jdij

由于取点的任意性 , 可以得到一个点(不为 1 , 记为 a )连接的所有边中的最短边是连向 1 的边

又因为一条不以 1 为端点的边会被类似 a 的点考虑两次 , 这启发我们从大到小分配边权 , 这样后面分配时就不用考虑前面分配过的边

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示当前和 1 相连的边已经被使用了 i 条 , 且目前分配过的最小值是 j (注意实际分配了 j 值的边数可能为 0 , 也就是做了前缀优化)

我们不考虑枚举下一条边使用了哪个值 , 因为当多条边使用同一个值时会很复杂 , 我们考虑有多少边使用值 j − 1 j-1 j1

l l l 为我们枚举的分配了 j − 1 j-1 j1 的边数 , 可以得到转移方程

d p [ i + l ] [ j − 1 ] = ∑ d p [ i ] [ j ] ⋅ ( n − i − 1 l ) ⋅ p o w ( m a x _ v a l − j + 2   ,   l ( n − 2 ) − ( l 2 ) − l ⋅ i ) dp[i+l][j-1] =\sum dp[i][j] \cdot \binom{n-i-1}{l} \cdot \mathrm{pow}(\mathrm{max\_val}-j+2\ ,\ l(n-2)-\binom{l}{2}-l\cdot i) dp[i+l][j1]=dp[i][j](lni1)pow(max_valj+2 , l(n2)(2l)li)

其中 p o w ( a , b ) = a b \mathrm{pow}(a,b) = a^b pow(a,b)=ab , m a x _ v a l \mathrm{max\_val} max_val 代表最大可使用的值 , 即题中所述的 k k k .

初始值为 d p [ 0 ] [ m a x _ v a l + 1 ] = 1 dp[0][\mathrm{max\_val}+1] = 1 dp[0][max_val+1]=1 .

解释一下

第二项的二项式系数 , 代表我们从还剩的连向 1 的边中要选择几条分配值 j − 1 j-1 j1

第三项的幂次 , 代表给所有会被这次选的连向 1 的边的那个非 1 顶点考虑到的不连向 1 的边赋值 j − 1 ∼ m a x _ v a l j-1 \sim \mathrm{max\_val} j1max_val

l ( n − 2 ) l(n-2) l(n2) 是所有相关的边的数量 , ( l 2 ) \binom{l}{2} (2l) 是前面一项里重复考虑的边的数量 , l ⋅ i l\cdot i li 是前面已经考虑过的边的数量

细节看代码

Code

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

std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;

#define ls (loc<<1)
#define rs ((loc<<1)|1)

const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int n, k;
ll dp[300][300];
ll C[303][303];

ll ksm(ll bs, int x) {
    ll ans = 1;
    while(x) {
        if(x & 1) ans = (ans * bs) % mod2;
        bs = (bs * bs) % mod2;
        x >>= 1;
    }
    return ans;
}

ll bino(ll x, ll y) {
    return C[x][y];
}

void init() {
    int t = 300;
    for(int i=0; i<=t; i++) { C[i][i] = 1; }
    for(int i=0; i<=t; i++) { C[i][0] = 1; }
    for(int i=1; i<=t; i++) {
        for(int j=1; j<=i; j++) {
            C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod2;
        }
    }
}

void solve(cint T) {
    cin >> n >> k;
    dp[0][k+1] = 1;
    for(int i=0; i<=n-2; i++) {
        for(int j=k+1; j>=2; j--) {
            if(dp[i][j]) {
                for(int l=n-1-i; l>=0; l--) {
                    int r = k-j+2;
                    dp[i+l][j-1] += (dp[i][j] * (bino(n-1-i, l) * ksm(r, l*(n-2)-bino(l, 2)-l*i) % mod2)) % mod2;
                    dp[i+l][j-1] %= mod2;
                }
            }
        }
    }
    ll ans = 0;
    for(int i=1; i<=k; i++) { ans = (ans + dp[n-1][i]) % mod2; }
    cout << ans << '\n';
}

int main() {
    freopen("1.in", "r", stdin);
    //cout.flags(ios::fixed); cout.precision(8);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T_=1;
    //std::cin >> T_;
    init();
    for(int _T=1; _T<=T_; _T++)
        solve(_T);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值