刷题之Educational Codeforces Round 125 (Rated for Div. 2)

刷题之Educational Codeforces Round 125 (Rated for Div. 2)

题单链接:刷题之Educational Codeforces Round 125 (Rated for Div. 2)

1657A. Integer Moves

  • 思路:对于任意长度 x 2 + y 2 \sqrt{x^2+y^2} x2+y2 线段,一定可以找到两条整数边组成三角形,那就可以通过两步跳到目标点。

    • 简单证明:考虑在线段两顶点为圆心,各自以整数长度为半径画两个可相交的圆,则圆交点距两顶点即为整数

    一开始看成必须经过整数点,差点就写bfs了

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, x, y;
int main(){
    // freopen("1.in","r",stdin);
    cin >> cas;
    while(cas--){
        cin >> x >> y;
        if(!x && !y) puts("0");
        else{
            int z = x * x + y * y;
            int sq = (int)sqrt(z);
            puts((sq * sq == z) ? "1" : "2");
        }
    }
}
/*
3
8 6
0 0
9 15
*/

1657B. XY Sequence

  • 思路:看着比较复杂需要找 x , y x,y x,y 的关系,但是发现 ∑ n ≤ 2 ⋅ 1 0 5 \sum n\le 2\cdot10^5 n2105,于是暴力一个一个加即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
ll cas, n, A, x, y;
ll sum, now;
int main(){
    // freopen("1.in","r",stdin);
    cin >> cas;
    while(cas--){
        cin >> n >> A >> x >> y;
        sum = now = 0;
        rep(i, 1, n){
            if(now + x <= A) now += x;
            else now -= y;
            sum += now;
        }
        cout << sum << endl;
    }
}
/*
3
5 100 1 30
7 1000000000 1000000000 1000000000
4 1 7 3
*/

1657C. Bracket Sequence Deletion

  • 思路:根据题目的两种目标(匹配 or 回文),可各自根据由 “ ( ( (” 或 “ ) ) )” 开头分成两种情况
    • ( ( (开头:若下一个位置为 “ ( ( (” 则是回文;若下一个位置为 “ ) ) )” 则是匹配。于是此情况只需要看连续两个位置。
    • ) ) )开头:此情况一定不可能匹配,则只能考虑回文,发现只要找到最近的下一个 “ ) ) )” 位置,则两者定为最短可删去的串。
    • 可统计一下每个 “ ) ) )” 的位置,然后从头根据上面两种情况 O ( n ) O(n) O(n) 遍历一遍即可。注意末尾终止状态
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, n;
int sum, now;
string s;
queue<int> qr;
int main(){
    // freopen("1.in","r",stdin);
    cin >> cas;
    while(cas--){
        cin >> n >> s;
        s = "0" + s;  //0 ~ n-1 改成 1 ~ n
        while(qr.size()) qr.pop();
        rep(i, 1, n){
            if(s[i] == ')') qr.push(i);
        }
        sum = 0, now = 1;
        while(1){
            if(now >= n) break;
            if(s[now] == '('){
                sum++;
                now += 2;
            }
            else{
                while(qr.size() && qr.front() <= now) qr.pop();
                if(!qr.size()) break;  //无法找到回文串,就直接结束
                sum++;
                now = qr.front() + 1; qr.pop(); 
            }
        }
        cout << sum << " " << n - (now - 1) << endl;
    }
}
/*
5
2
()
3
())
4
((((
5
)((()
6
)((()(
*/

1657D. For Gamers By Gamers

  • 知识点:二分

  • 思路

    • 首先,对于选择的一种士兵队伍,若想战胜怪兽需要

      士 兵 胜 利 所 用 时 间 = 士兵战力 怪 兽 血 量 > 怪 兽 战 力 士 兵 血 量 = 怪 兽 胜 利 所 用 时 间 士兵胜利所用时间=\dfrac{\text{士兵战力}}{怪兽血量}>\dfrac{怪兽战力}{士兵血量}=怪兽胜利所用时间 =士兵战力>=
      对于不同怪兽,只需要考虑比较 士 兵 战 力 × 士 兵 血 量 士兵战力\times 士兵血量 × 比较 $怪兽战力\times 怪兽血量 $ 即可。于是对于每种士兵,将其战力×血量记为其价值。

    • 其次,注意每次只能选择任意数量的同一种士兵,且保证花费尽量少,故可构建函数 f [ i ] f[i] f[i] 表示花费 i i i 最多可获得的价值。显然花费越多,价值越大,于是 f f f 函数单调上升。则只需要对每个怪兽二分找到大于怪兽价值的最小 f f f 即可。

    • 计算 f f f 函数:首先存下每种士兵只选一队的花费。其次从小到大更新,若某种士兵选多队对应的的最大价值比已知还大,则更新。

    • 判断是否非法时,将最大花费可获得的最大价值 f [ C ] f[C] f[C] 与怪兽所需价值比较,注意当相等时,说明士兵和怪兽同时死亡,也是不可以的。

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e6 + 5;
const db eps = 1e-10;
ll cas, n, m, C, c, d, h;
map<int, ll> mp;
ll f[N], now, ans;  //f[i]表示花费i最多可获得的val
int main(){
    // freopen("1.in","r",stdin);
    scanf("%lld%lld", &n, &C);
    rep(i, 1, n){
        scanf("%lld%lld%lld", &c, &d, &h);
        if(!mp.count(c)) mp[c] = d * h;
        else mp[c] = max(mp[c], h * d);
        f[c] = mp[c];
    }
    rep(i, 1, C){
        f[i] = max(f[i], f[i - 1]);
        if(!mp.count(i)) continue;
        rep(k, 1, C / i){
            f[i * k] = max(f[i * k], k * f[i]);
        }
    }
    cin >> m;
    rep(i, 1, m){
        scanf("%lld%lld", &d, &h);
        now = d * h;
        if(f[C] <= now){  //注意是小于等于!!!
            printf("-1 ");
            continue;
        }
        int l = 1, r = C + 1, mid;
        while(l + 1 < r){  //二分
            mid = (l + r) / 2;
            if(f[mid] <= now) l = mid;
            else r = mid;
        }
        ans = f[l] > now ? l : r;
        printf("%lld ", ans);
    }
}
/*
3 10
3 4 6
5 5 5
10 3 4
3
8 3
5 4
10 15

1 1
1 1 1
1
1 1
*/

1657E. Star MST

  • 思路:考虑最小生成树是以节点 1 1 1 为根,所有其他 n − 1 n-1 n1 个点为叶节点的两层的树。则要保证所有 2 ≤ u < v ≤ n − 1 2\le u<v\le n-1 2u<vn1,满足
    e d g e ( u , v ) ≥ m a x { e d g e ( 1 , u ) , e d g e ( 1 , v ) } = m a x n edge(u,v)\ge max\{edge(1,u), edge(1,v)\}=maxn edge(u,v)max{edge(1,u),edge(1,v)}=maxn
    于是边 e d g e ( u , v ) edge(u,v) edge(u,v) 的权值选择只有 m a x n ∼ k maxn\sim k maxnk 一共 k − m a x n + 1 \color{red}k-maxn+1 kmaxn+1 种。

    • 对于 e d g e ( 1 , u ) ,    2 ≤ u ≤ n edge(1,u),\;2\le u\le n edge(1,u),2un n − 1 n-1 n1 条边,考虑从大到小分配权值。构造函数 f [ r e s t ] [ m a x n ] f[rest][maxn] f[rest][maxn] 表示 n − 1 n-1 n1 个叶节点中还剩 r e s t rest rest 个点未选,且可赋值的最大值为 m a x n maxn maxn 的情况下,这 r e s t + 1 rest+1 rest+1 个点构成的完全图子图的方案数

    • 计算 f [ r e s t ] [ m a x n ] f[rest][maxn] f[rest][maxn]:考虑在点 1 1 1 到这 r e s t rest rest 个点的 r e s t rest rest 条边中,我们恰选取 t t t 条边权值为 m a x n maxn maxn,则有 C r e s t t C_{rest}^{t} Crestt 种选法。

      • 对于连接这 t t t 个点的剩余其他边,所取权值均需大于等于 m a x n maxn maxn。这些边分为两种,一种是 t t t 个点内部连接的 C t 2 C_{t}^{2} Ct2 条,另一种是 t t t 个点与剩余 r e s t − t rest-t restt 个点连接的 t ⋅ ( r e s t − t ) t\cdot (rest-t) t(restt) 条。共有 ( k − m a x n + 1 ) C t 2 + t ⋅ ( r e s t − t ) (k-maxn+1)^{C_t^2+t\cdot (rest-t)} (kmaxn+1)Ct2+t(restt) 种选择方案。
      • 此时确定了 t t t 个点,则剩余 r e s t − t rest-t restt 个点的方案数为 f [ r e s t − t ] [ m a x n − 1 ] f[rest-t][maxn-1] f[restt][maxn1]
      • 此外若 r e s t rest rest 条边种不存在权值为 m a x n maxn maxn 的边,则方案数为 f [ r e s t ] [ m a x n − 1 ] f[rest][maxn-1] f[rest][maxn1]

      于是可列出转移方程
      f [ r e s t ] [ m a x n ] = f [ r e s t ] [ m a x n − 1 ] + ∑ t = 1 r e s t ( r e s t t ) ⋅ ( k − m a x n + 1 ) C t 2 + t ( r e s t − t ) ⋅ f [ r e s t − t ] [ m a x n − 1 ] f[rest][maxn]=f[rest][maxn-1]+\sum\limits_{t=1}^{rest}\dbinom{rest}{t}\cdot(k-maxn+1)^{C_t^2+t(rest-t)}\cdot f[rest-t][maxn-1] f[rest][maxn]=f[rest][maxn1]+t=1rest(trest)(kmaxn+1)Ct2+t(restt)f[restt][maxn1]

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 3e2 + 5;
const db eps = 1e-10;
const int mod = 998244353;
int cas, n, k;
ll f[N][N], C[N][N];  //f[i][j]表示还剩rest个点未选,且可赋值的最大值为maxn的情况下,这rest + 1个点构成的完全图子图的方案数
ll power(ll a, ll b, ll mod){
    ll ans = 1;
    if(mod == 1) ans = 0;
    while(b > 0){
        if(b % 2 == 1) (ans *= a) %= mod;
        (a *= a) %= mod;
        b >>= 1;
    }
    return ans;
}
int main(){
    cin >> n >> k;
    rep(i, 0, 250){
        C[i][0] = 1;
        rep(j, 1, i){
            if(j * 2 > i) C[i][j] = C[i][i - j];
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
    rep(maxn, 0, k) f[0][maxn] = 1;  //初始化
    rep(rest, 1, n - 1){
        rep(maxn, 1, k){
            f[rest][maxn] = f[rest][maxn - 1];
            rep(t, 1, rest){
                f[rest][maxn] += C[rest][t] * power(k - maxn + 1, (C[t][2] + t * (rest - t)) % mod, mod) % mod * f[rest - t][maxn - 1] % mod;
                f[rest][maxn] %= mod;
            }
        }
    }
    cout << f[n - 1][k] << endl;
}
/*
3 2
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值