ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022黑龙江省赛题解(The 17th Heilongjiang Provincial Collegiate Programming Contest)

2022-07-14 10:34:33  阅读:302  来源: 互联网

标签:Provincial 17th chose tx ty int 题解 -- push


A - Bookshelf Filling

题意:

有a,b两种高度的书,a < b,a有n本,b有m本,全部竖着摆放在高度为h的书架上,现在要把至多(m - 1)本b书横着摆放在一些书的顶部。

问这样摆放之后的最小宽度是多少。

题解:

最简单的方法就是二分。这道题显然具有单调性,因为假设一个答案x可行,我们在中间抽出一本书放在右边,答案为x+1就可行。

check很简单,直接判断左边能放多少本就行了。

查看代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, n, m, h;
int check(int x) {
    int la = n, lb = x - n;
    int cnt = n + m - x;
    int cap = 0;
    cap += (b - a) * (n / b);
    if(cap >= cnt) return 1;
    cap += (h - b) * (x / b);
    return cap >= cnt;
}
void work() {
    cin >> a >> b >> n >> m >> h;
    int l = n + 1, r = m + n;
    while(l <= r) {
        int Mid = (l + r) / 2;
        if(check(Mid)) r = Mid - 1;
        else l = Mid + 1;
    }
    cout << l << endl;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int Case;
    cin >> Case;
    while(Case--) work();
    return 0;
}

C - Tree Division

题意:

问能否将树上每个点标上A,B的编号(黑白染色),同时满足:对于任意从根节点出发的路径,A点形成了严格递减序列,B点形成了严格递增序列。

题解:

一开始没想到树形dp,以为有啥构造方法。确定是树形dp之后就很简单了。

我们考虑子树方案,显然根节点会被染上一种颜色,假设是A。

我们发现,根节点一定是子树内最小的元素。而如果对于根节点在原树的父亲节点,假如它染成A颜色,只需要考虑他是不是比根节点小,如果染成B颜色,则需要考虑子树内最大的B颜色是多大,所以我们只需要考虑子树内最大的被染成B的元素是多大就行了。

最直观的方法是$f[i][j]$表示以i为根的子树,最大B元素是j,是否实现。这个显然浪费了很多空间和时间,我们发现只需要关注最大的一个B元素最小能达到多少就行了。

我们修正状态$f[i][0]$表示以i为根的子树,根节点被染成A颜色,时,子树内最大的B元素最小是多少。

同理,$f[i][1]$就是以i为根的子树,根节点被染成B颜色,时,子树内最小的A元素最大是多少。

只考虑$f[i][0]$,我们考虑它的每一棵子树进行转移。

如果子树的根节点大于当前节点,说明可以从$f[j][0]$进行转移;

如果$f[j][1]$大于当前节点,那么可以从子树树根的值进行转移。

两者先取最小,再跟根节点取最大值,就完成转移了。

查看代码
//
// Created by onglu on 2022/7/14.
//

#include <bits/stdc++.h>

#define all(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

#define endl '\n'
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define Mid ((l + r) / 2)
//#define int long long
using namespace std;
const int N = 2e6 + 1009;
//const int N = 2e5 + 1009;
//const int N = 5009;
//const int N = 309;
int n, m, a[N], f[N][2];
vector<int> ver[N];
void dfs(int x, int pre) {
    f[x][0] = -1;
    f[x][1] = n + 1;
    for(auto y : ver[x]) if(pre != y) {
        dfs(y, x);
        int chose = n + 1;
        if(a[y] > a[x]) {
            chose = min(chose, f[y][0]);
        }
        if(f[y][1] > a[x]) {
            chose = min(chose, a[y]);
        }
        f[x][0] = max(f[x][0], chose);

        chose = -1;
        if(a[y] < a[x]) {
            chose = max(chose, f[y][1]);
        }
        if(f[y][0] < a[x]) {
            chose = max(chose, a[y]);
        }
        f[x][1] = min(f[x][1], chose);
    }
}
void work() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        ver[x].push_back(y);
        ver[y].push_back(x);
    }
    dfs(1, 1);
    if(f[1][0] != n + 1 || f[1][1] != -1) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
}

signed main() {
#ifdef LOCAL
    freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.in", "r", stdin);
    freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int Case = 1;
    // cin >> Case;
    while(Case--) work();
    return 0;
}

F - 342 and Xiangqi

题意:

有两个没有区别的象在棋盘上,问走到对应的位置需要几步。

题解:

什么最短路,什么考虑象按顺序移动不影响,反正没几步就暴力bfs呗,懒得思考了。

查看代码
 #include <bits/stdc++.h>
#define endl '\n'
using namespace std;

vector<vector<int> > v;
int n, m, s, t;
map<pair<int, int>, int> M;
void work() {
    cin >> n >> m >> s >> t;
    if(s > t) swap(s, t);
    if(n > m) swap(n, m);
    M.clear();
    queue<pair<int, int> > q;
    q.push({n, m});
    M[{n, m}] = 0;
    while(q.size()) {
        int x = q.front().first;
        int y = q.front().second;
        int d = M[{x, y}];
        if(x == s && y == t) {
            cout << d << endl;
            return ;
        }
        q.pop();
        for(auto z : v[x]) if(z != y) {
            int tx = y, ty = z;
            if(tx > ty) swap(tx, ty);
            if(M.count({tx, ty})) continue;
            M[{tx, ty}] = d + 1;
            q.push({tx, ty});
        }
        for(auto z : v[y]) if(z != x) {
            int tx = x, ty = z;
            if(tx > ty) swap(tx, ty);
            if(M.count({tx, ty})) continue;
            M[{tx, ty}] = d + 1;
            q.push({tx, ty});
        }
    }
}
signed main()
{
    v.push_back(vector<int> {});
    v.push_back(vector<int> {2, 3});
    v.push_back(vector<int> {1, 4});
    v.push_back(vector<int> {1, 4});
    v.push_back(vector<int> {2, 3, 5, 6});
    v.push_back(vector<int> {4, 7});
    v.push_back(vector<int> {4, 7});
    v.push_back(vector<int> {5, 6});
    ios::sync_with_stdio(0);
    cin.tie(0);
    int Case;
    cin >> Case;
    while(Case--) work();
    return 0;
}

G - Chevonne's Necklace

 题意:

有一个环状序列,每个位置有一个值$c_i$,选定i位置的值,会导致它和它之后$(c_i-1)$个元素被删除(贡献是$c_i$),删除后序列会并拢,不会出现空隙。

问最多删除几个贡献,以及方案数。

两个方案不同,当且仅当被选定的位置的集合不同。

题解:

一开始没看到集合不同,想了快有两个小时。。。

这题似乎在lyd的书上见过,反正就是你任意选定总和不超过$n$的一个集合,你一定有办法把它删干净。

证明也很简单:因为总和不超过n,所以一定不存在循环依赖(你可以把每个元素想象成区间,假如存在循环依赖,总和一定大于n,因为每个元素都被覆盖至少一次)。

那么我们从一个一端元素只被覆盖一次的区间开始删除,那么就能删完了。

 

然后问题变成了选定总和不超过$n$的元素的方案数。裸的背包。

查看代码
 #include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int f[2009], g[2009], c[2009], n;
signed main()
{
    ios ::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    f[0] = 1;
    g[0] = 1;
    for(int i = 1; i <= n; i++) if(c[i] != 0) {
        for(int j = n - c[i]; j >= 0; j--) {
            f[j + c[i]] = (f[j + c[i]] + f[j]) % mod;
            g[j + c[i]] |= g[j];
        }
    }
    for(int i = n; i >= 0; i--) if(g[i]) {
        cout << i << " " << f[i] << endl;
        return 0;
    }
    cout << 0 << endl;
    return 0;
}

H - Kanbun

题意:

给定一个序列,包含'(','-',')',然后输出顺序是:

遇到左括号,跳过,直到输出与他匹配的右括号后,再输出它。

遇到其他元素直接跳过。

题解:

直接递归处理就行了。

先预处理出每个左括号对应的右括号,遇到左括号的时候,先递归输出区间内容,然后输出右括号,最后输出左括号。

查看代码
 #include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 1009;

int n, nxt[N];
char c[N];
void dfs(int l, int r) {
    if(l > r) return ;
    if(c[l] != '(') {
        cout << l << " ";
        dfs(l + 1, r);
    } else {
        dfs(l + 1, nxt[l] - 1);
        cout << nxt[l] << " ";
        cout << l << " ";
        dfs(nxt[l] + 1, r);
    }
}
signed main()
{
    ios ::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    cin >> (c + 1);
    stack<int> S;
    for(int i = 1; i <= n; i++) {
        if(c[i] == '(') S.push(i);
        else if(c[i] == '-') {
            nxt[i] = i;
        } else {
            nxt[S.top()] = i;
            nxt[i] = i;
            S.pop();
        }
    }
    dfs(1, n);
    return 0;
}

I - Equal Sum Arrays

题意:

给定一个数$n$,问有多少个正整数序列,满足序列所有元素和等于$n$。

题意:

n < 20,直接暴搜呗,手快的人这时候已经打完了。

正解的话,可以发现这就是n个数的隔板法,直接输出$2^{n - 1}$

查看代码
 #include <bits/stdc++.h>
using namespace std;
int n, ans = 0;
void dfs(int res) {
    if(res == 0) {
        ans += 1;
        return ;
    }
    for(int i = 1; i <= res; i++) {
        dfs(res - i);
    }
}
signed main()
{
    cin >> n;
    dfs(n);
    cout << ans << endl;
    return 0;
}

L - Let's Swap

题意:

给定一个字符串,我们可以进行以下操作:

将这个字符串$[1,x],[x + 1, n]$分别进行$reverse$。  

但是这个$x$只能取$l_1,l_2$,问给一个s,能否变成t。  

题解:

玩一下可能更能明白这道题的意思:

假设对$l_1$进行翻转称为A操作,另一个称为$B$操作。

我们发现每个操作的逆操作是自身,并且操作满足结合律。

也就是说ABBA = 没操作。

这样代表,我们的操作只能是ABABABABABABA或者BABABABABAB这样的

假设$A<B$,我们把AB看成一次操作,模拟一下字符串有哪些变化。

我们发现这次操作在原序列不变的基础上,把最前面(B-A)长度的串扔到了字符串末尾。

然后若干次循环以后,这个段又回到最开始,这就类似于更相减损法,所以这个操作的最小移动单元是gcd(n,B-A)。

我们枚举每一个长度为k*(B-A)的前缀移动到末尾之后,是否与t相同就行了。

这时候我们注意,还有一个操作是BABABAB,它是以B开头的,所以我们先进行一次B,然后就变成跟第一个操作一样的处理方法了。

查看代码
 #include <bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N = 5e5 + 1009;
const int mod = 257;
char s[N], t[N], tmp[N];
ull hs[N], pw[N];
int a, b, c, n;
ull ask(int l, int r) {
    return hs[l] - hs[r + 1] * pw[r - l + 1];
}
ull get(int pos) {
    return ask(pos, n) + ask(1, pos - 1) * pw[n - pos + 1];
}
int check() {
    ull tar = 0;
    pw[0] = 1;
    for(int i = 1; i <= n; i++) pw[i] = pw[i - 1] * mod;
    for(int i = n; i >= 1; i--) {
        tar = tar * mod + t[i];
    }
    hs[n + 1] = 0;
    for(int i = n; i >= 1; i--) {
        hs[i] = hs[i + 1] * mod + s[i];
    }
    for(int i = 1; i <= n; i += a) {
        if(tar == get(i + a)) {
            return 1;
        }
    }
    return 0;
}
void work() {
    cin >> s + 1 >> t + 1;
    cin >> a >> b;
    n = strlen(s + 1);
    c = a;
    a = max(a, b) - min(a, b);
    a = __gcd(a, n);
    if(check()) {
        cout << "yes" << endl;
        return ;
    }
    for(int i = 1; i <= n; i++) tmp[i] = s[i];
    int cnt = 0;
    for(int i = c; i >= 1; i--) s[++cnt] = tmp[i];
    for(int i = n; i > c ; i--) s[++cnt] = tmp[i];
    if(check())
        cout << "yes" << endl;
    else cout << "no" << endl;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int Case; cin >> Case;
    while(Case--) work();
    return 0;
}

标签:Provincial,17th,chose,tx,ty,int,题解,--,push
来源: https://www.cnblogs.com/onglublog/p/16476450.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有