Codeforces Round #642 (Div. 3)

Codeforces Round #642 (Div. 3)(2020.5.15)

这场昨天没打,今天补了一下发现很亏啊。这昨天要是打了不直接起飞(

光速出前四题可以加个100分左右。

A、Most Unstable Array

这A其实感觉比B更难一点。

题目要求非负整数数列,于是我们肯定考虑一大一小一大一小这样排列,这样能让每个绝对值对答案的贡献尽可能大。

然后我们假设从 m m m里拆了一个数 x x x出来,那么为了让绝对值最大,较小的那个值肯定取0。于是数列就会长成像 . . . , 0 , x , 0 , . . . ...,0,x,0,... ...,0,x,0,...这样,会发现 x x x对答案的最大贡献为 2 x 2x 2x。然后如果数列无限长,这拆出来的数对答案其实没有影响。因为反正最后累加起来都是 m m m

所以我们就干脆放个 0 , m , 0 0,m,0 0,m,0,这样使元素最少并且使答案最大。但此时需要三个元素。所以我们只要对长度为 1 1 1 2 2 2的两种情况特判一下就好。

而且这答案其实可以从给的样例里观察出来…找规律也是个好办法。

#include 
using namespace std;
typedef long long ll;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n, m; cin >> n >> m;
        if (n == 1) cout << 0 << endl;
        else if (n == 2) cout << m << endl;
        else cout << 2 * m << endl;
    }
    return 0;
}

B、Two Arrays And Swaps

这题 n n n的范围只有6e3。感觉只要不是特别离谱的方法怎么做都能做出来吧…

排个序之后用小的换大的就行了。

#include 
using namespace std;
typedef long long ll;
const int MAXN = 50;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n, k; cin >> n >> k;
        int a[MAXN], b[MAXN];
        for (int i = 1; i <= n; ++i) scanf("%d", a + i);
        for (int i = 1; i <= n; ++i) scanf("%d", b + i);
        sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + n);
        int l = 1, r = n;
        while (l <= k && a[l] < b[r]) a[l++] = b[r--];
        int sum = 0;
        for (int i = 1; i <= n; ++i) sum += a[i];
        cout << sum << endl;
    }
    return 0;
}

C、Board Moves

由对称性显然把所有的数移到正中间是最小的。

然后中间是0,周围第一圈是1,第二圈是2,然后依次类推加一加就好了。

数学推一下表达式。另外要注意循环变量开ll。

#include 
using namespace std;
typedef long long ll;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n; cin >> n;
        ll sum = 0;
        for (ll i = 1; i <= n / 2; ++i)
            sum += 8 * i * i;
        cout << sum << endl;
    }
    return 0;
}

D、Constructing the Array

这题稍微有点意思。想倒确实想了我一会儿,主要是一开始一直在考虑能不能用计算或者分治迅速确定每次填的位置然后直接填上去…

后来灵光一现想到了之前写的一个线段操作专题,然后就会做了…

总之思想就还是模拟。对于一段0,我们在中间改个数会把这段0分成两个部分,而这两个部分都是连续的0。所以我们可以看成把一个线段从中间剖开,再对剩下的和这两个剖好的线段进行操作。如果这么考虑的话这个操作是可以用优先队列进行优化的。不过要重载个小于号。

题解是用set写的。不是很懂为什么想到用set写…感觉这就是个优先队列啊

#include 
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 10;
struct seg
{
    int s, t;
};
bool operator <(seg a, seg b)//优先队列默认是大根堆,也就是说方向和小于号是反的。
{							 //注意判断的条件是大于还是小于。
    if (a.t - a.s == b.t - b.s) return a.s > b.s;
    else return a.t - a.s < b.t - b.s;
}
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int a[MAXN];
        int n; cin >> n;
        priority_queue <seg> que;
        que.push({1, n}); int num = 0;
        while (!que.empty())
        {
            seg p = que.top(); que.pop();
            int mid = (p.s + p.t) >> 1;
            a[mid] = ++num;
            if (mid - 1 >= p.s) que.push({p.s, mid - 1});
            if (mid + 1 <= p.t) que.push({mid + 1, p.t});
        } 
        for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
        cout << endl;
    }
    return 0;
}

E、K-periodic Garland

如果比赛会卡在这题。前四题可以出得飞快,这题我大概做了快2h…

前1h30min考虑的是个奇怪的算法,快倒是很快,不过照我之前这个算法这么写的话意思和原题义就不一样了…

写的时候我不断灵魂质问:这NM凭什么样例都过不了

后来发现问题之后考虑了一个dp,意外地顺利。思路很快就想出来了,不过因为今天满课程序是晚上写的…AC之后去看题解发现和我的想法差不多,但还是有点区别。具体可以自己去看题解,这里说一下我的想法。

考虑对于某一位有0和1两个状态,可以把这个作为状态的一维;另外处理到的位数也可以作为状态的一维。

所以我们定义状态: d p 0 [ i ] dp_0[i] dp0[i]为第 i i i位为 0 0 0时、并且 1 1 1 i i i位都满足题设条件时的最少步数, d p 1 [ i ] dp_1[i] dp1[i]为第 i i i位为 1 1 1时、并且 1 1 1 i i i位都满足题设条件时的最少步数。

之后考虑转移。对于 d p 0 [ i ] dp_0[i] dp0[i]来说没有什么限制条件,只要前 i − 1 i-1 i1位满足条件再保证这一位是 0 0 0就可以了。所以 d p 0 [ i ] = m i n ( d p 0 [ i − 1 ] , d p 1 [ i − 1 ] ) + ( s t a t e [ i ] = = ′ 1 ′ ) dp_0[i]=min(dp_0[i-1],dp_1[i-1])+(state[i]=='1') dp0[i]=min(dp0[i1],dp1[i1])+(state[i]==1)(这么写可以省个if,从tourist大哥的代码里偷师来的)。而对于第 i i i位为1的情况,这时就又要分类:如果此时是整个数列的第一个1,那么就要满足前面所有位为0且当前位为1;而若不是第一个1,那么就要满足这个1和前面的那个1相隔为 k k k并且中间全是0。

那么因为每次转移都要用到中间全为0这个条件,所以我们要把中间的1全部关掉,即要重复多次统计某一区间里满足某条件的个数。所以做个前缀和 s u m [ i ] sum[i] sum[i],表示前 i i i位出现1的次数。

那么我们转移方程就是 d p 1 [ i ] = m i n ( s u m [ i − 1 ] , d p 1 [ i − k ] + s u m [ i − 1 ] − s u m [ i − k ] ) + ( s t a t e [ i ] = = ′ 0 ′ ) dp_1[i]=min(sum[i-1],dp_1[i-k]+sum[i-1]-sum[i-k])+(state[i]=='0') dp1[i]=min(sum[i1],dp1[ik]+sum[i1]sum[ik])+(state[i]==0)

所以最后的答案就是 m i n ( d p 0 [ n ] , d p 1 [ n ] ) min(dp_0[n],dp_1[n]) min(dp0[n],dp1[n])

然后这道题就做完了。

#include 
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 10;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n, k; cin >> n >> k;
        static char state[MAXN]; cin >> state + 1;
        vector <int> sum(n + 1, 0);
        for (int i = 1; i <= n; ++i)
            sum[i] = sum[i - 1] + (state[i] == '1');
        vector <int> dp_0(n + 1, 0), dp_1(n + 1, 0);
        for (int i = 1; i <= n; ++i)
        {
            dp_1[i] = sum[i - 1] + (state[i] == '0');//为防止越界我们拆开来写
            if (i - k >= 1) dp_1[i] = min(dp_1[i], dp_1[i - k] + sum[i - 1] - sum[i - k] + (state[i] == '0'));
            dp_0[i] = min(dp_1[i - 1], dp_0[i - 1]) + (state[i] == '1');
        }
        cout << min(dp_1[n], dp_0[n]) << endl;
    }
    return 0;
}

你可能感兴趣的:(Codeforces Round #642 (Div. 3))