Codeforces Round #797 (Div. 3) A-G

Codeforces Round #797 (Div. 3) A-F

https://codeforces.com/contest/1690

就是一个codeforces(颁奖台)的形状;中间的 最高,左边第二,右边第三
然后要使得中间的尽可能小,那么就按照等差数列来构造处理。
最理想情况下是 \(x-1, x, x-2\),且满足\(3x-3=n\),即\(x=\frac{n+3}{3}\)
然而会有些许偏差,所以我们根据样例作出适当调整,即x向上取整,然后按照\(x-1,x,n-x-x+1\)\(x=\frac{n+3+2}{3}\)来构造
注意每一项都要>0,所以特判一下右边为0的时候,要把左边挪一个过来
over
总之就是注意一些小细节
Code:

#include <bits/stdc++.h>

using namespace std;

void solve () {
    int n;
    cin >> n;
    int x = (n+3+2) / 3;
    int a = x-1, c=n-x-x+1;
    if (c == 0)
        a--, c++;
    cout << a << ' ' << x << ' ' << c << endl;
}

int main  () {
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}

B. Array Decrements

题意:每次可以把数列a中所有大于0的项减1,问能否通过若干次操作使得a的每一项都等于b
思路:如果对应项b是0的话,那么该点处a的可减上限是\(\infty\)(因为减到0之后就不对他进行操作了,我们可以称这样的点为无下限点
所以能做的就是统计有下限(也就是\(b_i\neq 0\)\(a_i\)所需改变的最大值\(dx\),如果出现 有下限点所需操作 不等于\(dx\)的情况,那么必定是不可能的。他已经达到要求了,别的数列还要减)
呃我语言表达能力不太行,可以看看代码,会好懂很多

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

using namespace std;
const int N = 5e4 + 5;
int a[N], b[N], n;

void test () {
    for (int i = 1; i <= n; i ++)
        cout << a[i] << ' ';
    cout << "\n";
}

void solve () {
    bool suc =  true;
    cin >> n;
    for (int i = 1; i<= n; i  ++)
        cin >> a[i];
    int dx = -1; //统计最少要减多少个
    for (int i = 1; i <= n; i ++) {
        cin>> b[i], a[i] -= b[i];
        if (a[i] >= 0)   dx = max (dx, a[i]); //注意可以取等
        if (a[i] < 0)
            suc = false;
    }
        
    if (!suc) {
        cout << "NO\n";
        return ;
    }
    
    //test ();

    
    for (int i = 1; i <= n; i ++) {
        //cout << dx << ' ';
        //如果b[i]是0的话就可以无限减,如果不是则不行
        if (b[i]) {
            if (dx != a[i]) {
                cout << "NO\n";
                return ;
            }
        }
    }
    cout << "YES\n";
}

int main  () {
    IOS;
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}
//ai<bi 肯定不行
//差值必须要相等(减到0的可以不等)

//复盘一下,要小心一些细节

C. Restoring the Duration of Tasks

简单模拟题。
题意:有若干个任务,每个任务都有计划起始时间\(a_i\)和计划完成时间\(b_i\),而在实际处理的过程中,如果当前任务还没有处理完,就会先把当前的做完再接着去做下一个(任务终止点不变,但是如果有前面的任务覆盖了的话,就从最近的上一个任务的结束点开始算)
思路:所以就按顺序遍历右端点,然后找到距离右端点最近的上一个结束点,如果找到的点在该段之外(没有重叠)就直接按完整区间算,否则用找到的右端点当作新起始点
似乎又说复杂了。。看代码就能懂的:

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int a[N], b[N];

// struct Node {
//     int idx, l, r;
//     bool 
// }a[N];

void solve () {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++)
        cin >> a[i];
    for (int i = 1; i <= n; i ++)
        cin >> b[i];

    cout << b[1] - a[1] << ' ';
    for (int i = 2; i <= n; i ++) {
        bool find = false;
        for (int j = i - 1; j >= 1; j --) {
            // if (b[j] <= a[i])
            //     break;
            if (b[j] > a[i]) {
                find = true;
                cout << b[i] - b[j] << ' ';
                break;
            }
        }
        if (!find)
            cout << b[i] - a[i] << ' ';
    }
    cout << endl;
    
}

signed main  () {
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}

//出现区间重叠:
//右端点不动,左端点变为距离右端点最近的上一个右端点(且一定要在左边)

D. Black and White Stripe

题意:给定一个字符串s,问要把多少个W变成B,才会有k个连续的B
思路:利用前缀和,统计每一段内的含'W'量,然后扫s中每一段长度为k的段,记录其中含'W'量的最小值,就是答案!
很巧妙的思路啊,我要积累一下(记小本本)

注意不用每次都更新cnt[],防止超时

#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
int cnt[N]; //记录B的个数

void solve () {
    int n, k;
    string s, t;
    cin >> n >> k >> s;
    s = ' ' +  s;

    for (int i = 1; i <= n; i ++)
        cnt[i] = cnt[i - 1] + (s[i] == 'W'); //cnt[0]一直是0,所以不需要memset
    int ans = 0x7fffffff;
    for (int i = k; i <= n; i ++)
        ans = min (ans, cnt[i] - cnt[i - k]);
    cout << ans << endl;
   
}

int main  () {
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}
//要把多少个W变成B,才会有k个连续的B

//首先已经有了长度>=k的段了,那么就直接0
//用最长的往两边拓展

//前缀和

E. Price Maximization

题意:给出数列\(a_n\),任意两两\(a_i,a_j\)组成一组,使得所有\(\frac{a_i+a_j}{k}\)之和最小
思路:首先统计每一个数中的含k量,然后记录每一个余数,两两组合拼在一起,再看能凑出多少个k
具体操作:排序 + 双指针两头扫
!!要开long long

#include <bits/stdc++.h>
#define int long long //传统美德不能忘啊
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

using namespace std;
const int N = 2e5 + 5;

void solve () {
    int n, k, ans = 0;
    cin >> n >> k;
    vector <int> v;
    for (int i = 1; i <= n; i ++) {
        int x;
        cin >> x;
        ans += x / k;
        if (x % k)
            v.push_back (x % k);
    }
    //cout << ans << endl;

    if (v.empty ()) {
        cout << ans << endl;
        return ;
    }

    sort (v.begin (), v.end ());
    n = v.size ();
    for (int i = 0, j = n - 1; i < j; ) {
        if (v[i] + v[j] >= k)
            ans += (v[i] + v[j]) / k, i ++, j --; //这里写成ans++, i++, j++ 也没问题的
        else    
            i ++; //凑不出,j不动,i往后挪肯定是最优的,因为按照升序排列
    }
    cout << ans << endl;

    // for (auto i : v)
    //     cout << i << ' ';
    // cout << endl;


}

signed main () {
    IOS;
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }

}

//看余数能凑出多少个k即可

F. Shifting String

题意:字符串s按照p[i]的规则进行重新排列,问至少要操作该过程多少次,才能恢复原串
把变换后的串构造出来,然后找循环节,求LCM

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

using namespace std;

void solve () {
    int n;
    string s;
    cin >> n >> s;
    vector <int> p(n + 1);
    vector <bool> vis(n + 1);
    for (int i = 1; i <= n; i ++)
        cin >> p[i];
    s = ' ' + s;
    
    int ans = 1;
    for (int i = 1; i <= n; i ++) {
        if (vis[i]) continue;
        string t; //匹配
        for (int j = i; !vis[j]; j = p[j])
            vis[j] = true, t += s[j]; //构造变换后的串
        
        int m = t.size ();
        for (int i = 1; i <= m; i ++) {
            if (m % i)  continue;
            bool suc = true;
            for (int j = 0; j < m && suc; j ++) {
                if (t[j] != t[(j + i) % m])
                    suc = false; //并非循环节
            }
            if (suc) {
                ans = lcm (ans, i);
                break;
            }
        }
    }
    cout << ans << endl;
}

signed main () {
    IOS;
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}
//置换群+s
//求每个环回到最初状态所需步数的lcm

G. Count the Trains

题意:给定序列an,满足对于所有\(i\geq1\),有\(a_{i-1}>= a_i\)。然后有操作,\(a_k=a_k-d\),要满足该操作之后,序列仍满足要求。(在每次操作过后,如果后一项比前一项大的话,就让他等于前一项)求每次操作过后,数列中不相等的数的个数。
思路:统计\(differ\),然后又有重复的要考虑,所以很容易想到map来维护。在每次更新之后都要"前瞻后顾",即 找的到前面有比它小的,就把找到的这个删掉;然后看后面,把比它大的删掉。
(前面只删一个,后面一直删)

\(Code reference: jls\)

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
#define IOS ios::sync_with_stdio (0);cin.tie(0);cout.tie (0);

using namespace std;
const int N = 1e5 + 5;
int a[N];
int n, m;
map<int, int> mp;

void add (int x, int ax) {
    mp[x] = ax;
    auto it = mp.find (x);

    if (it != mp.begin () && ax >= prev(it)->second) //相等的也要移除
        mp.erase (it);
    
    else {
        while (next(it) != mp.end () && ax <= next (it)->second)
            mp.erase (next(it));
    }
}

void solve () {
    mp.clear ();
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        add (i, a[i]);
    }
    while (m --) {
        int idx, k;
        cin >> idx >> k;
        a[idx] -= k;
        add (idx, a[idx]);
        cout << mp.size () << ' ';
    }
    cout << endl;
}

signed main () {
    IOS;
    int t;  cin >> t;
    while (t --) {
        solve ();
    }

}

看到这个绿绿的就是心情舒畅!

posted @ 2022-06-08 12:48  Sakana~  阅读(94)  评论(1编辑  收藏  举报