Codeforces Round 909(Div.3)


Codeforces Round 909(Div.3)
  • 总结:菜就多练。还是要少刷水题,多学知识才好成长,水题写多了只有签到的速度快了,但上限依旧低。
A. Game with Integers

A

800 —— 思维,简单博弈

题意

两人游戏。给定一个整数 n。操作为给整数+1或者-1,如果操作后的整数能被 3 整除,那么操作的人获胜。

解题思路

一眼题。除非开始就能被 3 整除,否则先手必胜。因为余 1,余 2 的情况先手都只需要操作一步即可。

(感觉自己越来越会蒙了?上手全凭直觉。唉唉… 赛事用了两分钟,然后开始坐牢)

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define db double
#define pb push_back

typedef long long ll;
typedef pair<int,int> PII;

const int N = 1e6 + 10;

int n, m; 

void solve()    
{
    cin >> n;
    if (n % 3 != 0) cout << "First" << endl;
    else cout << "Second" << endl;
}

int main()  
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin >> t; while (t -- )
    solve();

    return 0;
}
B. 250 Thousand Tons of TNT

B

1100 —— 暴力枚举?预处理

题意

给定 n 个箱子,以及每个箱子的重量。需要将箱子转到卡车上,保证每个卡车上都正好放 k 个箱子,k的取值1 ~ n。

问任意两辆卡车总重量的最大差值

解题思路

直接暴力肯定是超时的了。所以我们预处理出 n 的所有因子,然后开始暴力()。

对于每个因子 t ,也就是可以把 n 个箱子装到 n / t 辆卡车上,次数,我们把所有的重量存入,然后排序。就可以求出对于因子 t 的最大差值。对于每一个因子求最大差值,最后结果也就是最大差值。

悲报,以为初始箱子可以排序,样例一测,错了}。(赛事28分钟过的,难道我真的这么菜?沃趣,真的这么菜。)

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define db double
#define pb push_back

typedef long long ll;
typedef pair<int,int> PII;

const int N = 150010;

int n, m; 
ll a[N];
ll s[N];

void solve()    
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    //预处理前缀和
    for (int i = 1; i <= n; i ++ ) s[i] = a[i] + s[i - 1];

    //预处理因子
    vector<int> c;
    c.pb(1); c.pb(n);
    for (int i = 2; i <= n / i; i ++ )
    {
        if (n % i == 0)
        {
            c.pb(i); 
            if (i * i != n) c.pb(n / i);
        }
    }

    //遍历每一个因子
    ll mx = 0;
    for (auto t : c)
    {
        //每个因子下的每个卡车的重量
        vector<ll> f;
        for (int i = t; i <= n; i += t)
        {
            ll x = abs(s[i] - s[i - t]);
            f.pb(x);
        }   

        if (t == n) continue;

        //排序,然后取最大差值
        sort(f.begin(), f.end());
        ll now = f.back() - f.front();
        //更新最大值
        mx = max(mx, now);
    }

    cout << mx << endl;
}

int main()  
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin >> t; while (t -- )
    solve();

    return 0;
}
C. Yarik and Array

C

1100 —— 简单DP

题意

给定 n 个元素组成的数组 a,需要求出非空子数组最大值。要求子数组种不能有相邻的数拥有相同的奇偶性。

解题思路

都说是一眼题,简单DP。但我是DP低手?不,我是DP无能选手。DP方面真的跟小白一样,或许本身就是小白。唉,唉唉。

我是怎么想的呢,预处理出来每个断点,也就是相邻数相同奇偶性的点。然后预处理出来每个负数的点。可现实总是残酷的。如何考虑覆盖多少负数,如果每次在区间内的每个负数都要考虑覆盖,那无疑就太麻烦了。而这是B,或许说应该是比较好想的才对。然后,remake了。

DP的表示就是 以 i 为结尾的子序列的最大和,状态转移方程为:$dp[i] = max(a[i], dp[i - 1] + a[i])$ ,也就是要么继续连续,要么就重新连续。只需要加特判可不可以连续即可。

D了多久呢,赛事1时18分,从思路错误到转写dp,坐牢50分钟,而且还不是自己做出来的。唉,唉唉。DP低手。该练练了。(一个DP每开出过)

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define db double
#define pb push_back

typedef long long ll;
typedef pair<int,int> PII;

const int N = 2e5 + 10;

int n, m; 
int a[N];
//dp[i]是以i结尾子序列的最大和
int dp[N];

void solve()    
{
    cin >> n;
    int flag = false;

    //先处理出来单个数的最大值
    int mx = -0x3f3f3f3f;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> a[i];
        mx = max(a[i], mx);
        if (a[i] > 0) flag = true;
    }

    //如果全是负数,那就直接输出最大值即可。
    if (flag == false)
    {
        cout << mx << endl;
        return ;
    }

    //初始化dp[1] = (0, a[1])
    dp[1] = max(0, a[1]);
    for (int i = 2; i <= n; i ++ )
    {
        //如果不能连续,那就考虑重新连续。
        if (abs(a[i]) % 2 == abs(a[i - 1]) % 2) dp[i] = max(0, a[i]);
        //状态转移
        else dp[i] = max(a[i], dp[i - 1] + a[i]);
        //更新最大值
        mx = max(mx, dp[i]);
    }

    cout << mx << endl;
}

int main()  
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin >> t; while (t -- )
    solve();

    return 0;
}

D. Yarik and Musical Notes

D

1300 —— 数论?思维?白给!

题意

总结下来,就是有 n 个音符,给出每个音符代表的数字 $a_i$ ,那么音符 i 就是 $b_i = 2^{a_i}$ ,现在要求有多少对音符$b_i,b_j (i<j)$ ,组合($b_i, b_j$) = ($b_j,b_i$),组合($b_i,b_j$) = $b_i^{b_j}$。找到有多少对这样的音符。

解题思路

感觉题面很牛马… 写一下就可以发现只有 i,j = 1,2的时候是特殊条件,其他情况下只有 i == j,才满足组合条件。

怎么说呢,题有一,错误有二(),第一个错误是计算错了,导致一直以为不止一个特殊条件,然后陷入自我怀疑? 第二个错误是没有看见 j < i,然后怀疑起了样例?迷之自信。唉,唉唉。(靠外力赛事1时58分,坐牢40分钟,其实这种题能想这么久,也是一种能力?)

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define db double
#define pb push_back

typedef long long ll;
typedef pair<int,int> PII;

const int N = 2e5 + 10;

int n, m; 
int a[N];

void solve()    
{
    cin >> n;
    map<int,int> mp;
    for (int i = 0; i < n; i ++ )
    {
        cin >> a[i];
        mp[a[i]] ++ ;
    }

    ll sum = 0;
    //因为i < j,所以......
    for (int i = 0; i < n; i ++ )
    {
        mp[a[i]] -- ;
        if (a[i] == 1 || a[i] == 2)
        {
            sum += mp[1] + mp[2];
        }
        
        else sum += 1ll * mp[a[i]];
    }

    cout << sum << endl;
}

int main()  
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin >> t; while (t -- )
    solve();

    return 0;
}
E. Queue Sort

E

1300—— 贪心?

题意

一个由 n 个数组成的数组 a,需要通过操作变成升序排列。

每次操作:将第一个元素放入末尾,然后将这个元素与前一个元素对调,直到他严格大于前一个元素。

需要找出最小操作次数

解题思路

如果操作最小的数,那么操作就会无效,因为对原数组没有影响。

所以需要保证最小的数后面的序列是升序排列,才可能完成整个数组的排序。而最小的操作次数,也就是最小的数前面的数每个数都操作一次即可。

怎么评价呢,赛后写的,感觉比BCD简单多了……,可能直觉? 给我带来迷之自信。

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define db double
#define pb push_back

typedef long long ll;
typedef pair<int,int> PII;

const int N = 2e5 + 10;

int n, m; 
int a[N];

void solve()    
{
    cin >> n;
    //idx表示第一个最小值下标
    int mi = 0x3f3f3f3f, idx = 0;
    for (int i = 0; i < n; i ++ )
    {
        cin >> a[i];
        if (a[i] < mi) mi = a[i], idx = i;
    }

    //判断最小值后面的数是不是升序排列
    bool flag = true;
    for (int i = idx; i < n - 1; i ++ ) 
        if (a[i + 1] < a[i]) flag = false;

    if (flag == false)
    {
        cout << -1 << endl;
        return ;
    }

    cout << idx << endl;
}

int main()  
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin >> t; while (t -- )
    solve();

    return 0;
}
F. Alex's whims

F

1600 —— 构造

题意

给定一个 n 个顶点的树,有n - 1条边。树中的两个顶点的距离是两个顶点所经过的边数。

有 q 天,每一天会选择一个距离 d,需要保证这一天有两个距离正好为 d 的叶子。

为了保证每一天都有那个距离,给定一个操作:选择三个点 $u,v_1,v_2$,需要保证 $u,v_1$ 有一条边,$u,v_2$ ,没有边。操作就是删除 $u,v_1$,的边,连接 $u, v_2$ 的边。

保证满足条件的树和操作序列总是存在的。

解题思路

先不谈正解,这个破样例就看了半天。然后样例也不懂,就润了。但不得不说,构造的方法很妙,但感觉有点经典。可我又想不到……

正解:初始将所有点连成一条链,每次操作去改变节点 1 的位置,然后记录节点 1 所连的节点,长度记录为节点 1 到 最后一个点的位置。对于每次询问,我们只需要去改变节点 1 的位置,或者不改变,就可以满足长度 d。

感觉记录节点 1 到所连接的节点位置 i 比较好理解,长度就是 i - 1,画个图就明白了。可这样有点bug,所以倒着来,记录节点 1 到尾节点的位置就不会有bug。

代码

#include <bits/stdc++.h>
using namespace std;
 
void solve()    
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i < n; i ++ ) cout << i << " " << i + 1 << endl;
    
    //初始连接的节点2
    int v = 2;
    while (m -- )
    {
        int x;
        cin >> x;
        int len = (n - v) + 1;

        if (len == x)
        {
            cout << "-1 -1 -1\n";
            continue;
        } 

        else cout << 1 << " " << v << " " << n - x + 1 << endl;
        v = n - x + 1;       
    }        
}

int main()  
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    
    int t; cin >> t; while (t -- )
    solve();

    return 0;
}

文章作者: han yue
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 han yue !
评论
  目录