Codeforces Round #851 (Div. 2) 题解记录

Codeforces Round #851 (Div. 2)

B. Sum of Two Numbers

给定一个整数n,找出两个满足下列条件的非负整数x和y。

  • x + y = n x+y=n x+y=n
  • x x x的位数之和与 y y y的位数之和最多相差1。

输出任意一组 x , y x,y x,y

数据范围:多组数据 t , 1 ≤ t ≤ 10000 , 1 ≤ n ≤ 1 0 9 t,1 \le t \le 10000,1 \leq n \leq 10^9 t,1t10000,1n109

input

5
1
161
67
1206
19

output

1 0
67 94
60 7
1138 68
14 5

Note

In the second test case, the sum of digits of 67 and the sum of digits of 94 are both 13.

In the third test case, the sum of digits of 60 is 6, and the sum of digits of 7 is 7.

思路:

对于第 i i i位数字 d i d_i di,如果 d i d_i di是偶数,则 x , y x,y x,y各获得 d i 2 \frac{d_i}{2} 2di,如果是奇数,则 x x x获得 d i 2 \frac{d_i}{2} 2di y y y获得 d i + 1 2 \frac{d_i+1}{2} 2di+1,下一次遇到奇数则让 y y y获得 d i 2 \frac{d_i}{2} 2di x x x获得 d i + 1 2 \frac{d_i+1}{2} 2di+1,这样可以保证 x , y x,y x,y的数位之和之差不超过1

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<map>
#include<set>
#include<functional>
#include<unordered_set>
#include<queue>
#include<stack>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 5e4 + 5;
int main()
{
    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);
    int t;
    cin >> t;
    while (t--) {
        string s;
        cin >> s;
        bool flag = 1;
        string a, b;
        for (int i = 0; i < s.size(); i++) {
            int t = s[i] - '0';
            if (t % 2 == 0) {
                a += (t / 2) + '0';
                b += (t / 2) + '0';
            }
            else if (flag) {
                a += (t / 2) + '0';
                b += (t / 2 + 1) + '0';
                flag = 0;
            }
            else {
                b += (t / 2) + '0';
                a += (t / 2 + 1) + '0';
                flag = 1;
            }
        }
        cout << stoi(a) << " " << stoi(b) << "\n";
    }
    return 0;
}

C. Matching Numbers

给定一个 1 ∼ 2 n 1\sim2n 12n的排列,将 1 1 1 2 n 2n 2n的整数两两配对,变成 { a 1 , b 1 , a 2 , b 2 , . . . , a n , b n } \{a_1,b_1,a_2,b_2,...,a_n,b_n\} {a1,b1,a2,b2,...,an,bn},满足 { a 1 + b 1 , a 2 + b 2 , . . . , a n + b n } \{a_1+b_1,a_2+b_2,...,a_n+b_n\} {a1+b1,a2+b2,...,an+bn}为严格递增序列,且相邻差值为1,即 a 1 + b 1 + 1 = a 2 + b 2 a_1+b_1+1=a_2+b_2 a1+b1+1=a2+b2 a 2 + b 2 + 1 = a 3 + b 3 a_2+b_2+1=a_3+b_3 a2+b2+1=a3+b3,以此类推。

数据范围:多组数据 t t t 1 ≤ t ≤ 500 , 1\le t\le500, 1t500, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105,n的总和不超过 1 0 5 10^5 105

思路:

由等差数列求和公式:
n ( s 1 + s 1 + n − 1 ) 2 = 2 n ( 1 + 2 n ) 2 s 1 = 3 + 3 n 2 \frac{n(s_1+s_1+n-1)}{2}=\frac{2n(1+2n)}{2}\\ s_1=\frac{3+3n}{2} 2n(s1+s1+n1)=22n(1+2n)s1=23+3n
n n n为偶数时,无解

n n n为奇数时,对小数据暴力后可发现规律,
{ a 1 , a 2 , . . . , a n } = { 1 , 2 , . . . , n } { b 1 , b 2 , . . . , b n } = { n + 1 + n 2 , n + 2 + n 2 , . . . , 2 n , n + 1 , n + 2 , . . . , n + n 2 } \{a_1,a_2,...,a_n\}=\{1,2,...,n\}\\ \{b_1,b_2,...,b_n\}=\{n+1+\frac{n}{2},n+2+\frac{n}{2},...,2n,n+1,n+2,...,n+\frac{n}{2}\} {a1,a2,...,an}={1,2,...,n}{b1,b2,...,bn}={n+1+2n,n+2+2n,...,2n,n+1,n+2,...,n+2n}

{ a 1 , a 2 , . . . , a n } = { 1 , 2 , 3 , 4 , 5 } { b 1 , b 2 , . . . , b n } = { 8 , 9 , 10 , 6 , 7 } { s 1 , s 2 , . . . , s n } = { 8 + 1 , 6 + 4 , 9 + 2 , 7 + 5 , 10 + 3 } = { 9 , 10 , 11 , 12 , 13 } \{a_1,a_2,...,a_n\}=\{1,2,3,4,5\}\\ \{b_1,b_2,...,b_n\}=\{8,9,10,6,7\}\\ \{s_1,s_2,...,s_n\}=\{8+1,6+4,9+2,7+5,10+3\}=\{9,10,11,12,13\} {a1,a2,...,an}={1,2,3,4,5}{b1,b2,...,bn}={8,9,10,6,7}{s1,s2,...,sn}={8+1,6+4,9+2,7+5,10+3}={9,10,11,12,13}

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<map>
#include<set>
#include<functional>
#include<unordered_set>
#include<queue>
#include<stack>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 5e4 + 5;
int main()
{
    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        if (n % 2 == 0) {
            cout << "No" << "\n";
            continue;
        }
        int j = 1;
        cout << "Yes" << "\n";
        if (n == 1) {
            cout << "1 2" << "\n";
        }
        else {
            for (int i = n + 1 + n / 2; i <= 2 * n; i++) cout << j++ << " " << i << "\n";
            for (int i = n + 1; i < n + 1 + n / 2; i++) cout << j++ << " " << i << "\n";
        }
    }
    return 0;
}

D. Moving Dots

给定数轴上的 n n n个点,第 i i i个点的初始坐标是 x i x_i xi。这些坐标是不同的。每个点以相同的恒定速度同时开始移动。每个点都向最近的点(相邻两点,坐标之差的绝对值最小)的方向移动,直到它遇到另一个点。在平局(与左右两点坐标之差的绝对值相同)的情况下,它向左移动,两个点碰撞之后就会停止移动。经过足够的时间,每个点都会停止移动。游戏的结果是点停止时的不同坐标的数量

我们需要计算所有大于两个点的子集结果的总和。由于结果可能非常大,所以要打印 1 0 9 + 7 10^9+7 109+7的模数之和。

数据范围: 2 ≤ n ≤ 3000 , 1 ≤ x 1 < x 2 < … < x n ≤ 1 0 9 2 \leq n \leq 3000,1 \leq x_1 < x_2 < \ldots < x_n \leq 10^9 2n3000,1x1<x2<<xn109

Examples

input

4
1 2 4 6

output

11

input

5
1 3 5 11 15

output

30

在第一个例子中,对于一个大小为2的子集,两个点向对方移动,所以有一个坐标是碰撞点。

对于大小为3的子集,第一个点和第三个点向第二个点移动,因此,无论第二个点移动的方向如何,都有一个坐标让两个点停下来。

对于 [ 1 , 2 , 4 , 6 ] [1,2,4,6] [1,2,4,6],第一个点和第二个点向对方移动。对于第三个点,最初,第二个点和第四个点是最接近的点。由于它是一个平局,第三个点向左移动。第四个点也向左移动。因此,碰撞点是1.5

因为有6个大小为2的子集,4个大小为3的子集和1个大小为4的子集,所以答案是 6 ⋅ 1 + 4 ⋅ 1 + 1 = 11 6⋅1+4⋅1+1=11 61+41+1=11

思路:

考虑枚举碰撞情况,计算每两个点碰撞对答案产生的贡献,也即有多少种情况, i , j i,j i,j两点直接相撞


如上图所示,4和1碰撞,那么4和1中间的点必然不选,同时需要满足4不会往左撞,1不会往右撞,4与1的距离是3,那么1需要排除右边两个点,至于图中绿色的点可选可不选,它们不影响碰撞结果,所以4和1碰撞产生的贡献为 2 3 ∗ 2 1 = 16 2^3*2^1=16 2321=16

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<map>
#include<set>
#include<functional>
#include<unordered_set>
#include<queue>
#include<stack>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
typedef long long ll;
int mod = 1e9 + 7;
ll qmi(ll a, ll b, ll p) {
    ll res = 1;
    while (b) {
        if (b & 1) res = (res * a) % p;
        b >>= 1;
        a = (a * a) % p;
    }
    return res;
}
int main()
{
    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);
    int n;
    cin >> n;
    ll ans = 0;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        int l = i - 1, r = i + 1;
        // 枚举i与j相撞,i和j中间的点不选
        for (int j = i + 1; j <= n; j++) {
            int d = a[j] - a[i];
            // i一定不能往左边撞
            while (l >= 1 && a[i] - a[l] <= d) --l;
            // j一定不能往右边撞
            while (r <= n && a[r] - a[j] < d) ++r;
            ans = (ans + qmi(2, l, mod) * qmi(2, n - r + 1, mod)) % mod;
        }
    }
    cout << ans;
    return 0;
}

E. Sum Over Zero

给你一个n个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an的数组。考虑 S S S是一组满足以下条件的线段。 S S S的每个元素应该是 [ x , y ] [x,y] [x,y]的形式,其中 x x x y y y 1 1 1 n n n之间的整数,包括端点在内,并且 x ≤ y x≤y xy。在 S S S中所有线段互不相交,且对于 S S S中的每个 [ x , y ] [x, y] [x,y] a x + a x + 1 + … + a y ≥ 0 a_x+a_{x+1}+ \ldots +a_y \geq 0 ax+ax+1++ay0
线段 [ x , y ] [x,y] [x,y]的长度被定义为 y − x + 1 y-x+1 yx+1 f ( S ) f(S) f(S)被定义为 S S S中每条线段的长度之和,即 f ( S ) = ∑ [ x , y ] ∈ S ( y − x + 1 ) f(S) = \sum_{[x, y] \in S} (y - x + 1) f(S)=[x,y]S(yx+1)。注意,如果 S S S是空的, f ( S ) f(S) f(S) 0 0 0

在所有可能的 S S S中,最大的 f ( S ) f(S) f(S)是多少?

数据范围: 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \leq n \leq 2 \cdot 10^5 1n2105, − 1 0 9 ≤ a i ≤ 1 0 9 -10^9 \leq a_i \leq 10^9 109ai109

input

5
3 -3 -2 5 -4

output

4

input

10
5 -2 -4 -6 2 3 -6 5 3 -2

output

9

input

4
-1 -2 -3 -4

output

0

在第一个例子中, S = { [ 1 , 2 ] , [ 4 , 5 ] } S=\{[1, 2], [4, 5]\} S={[1,2],[4,5]}可能是一个 S S S,因为 a 1 + a 2 = 0 a_1+a_2=0 a1+a2=0 a 4 + a 5 = 1 a_4+a_5=1 a4+a5=1 S = { [ 1 , 4 ] } S=\{[1, 4]\} S={[1,4]}也可以是一个可能的解决方案。

由于不存在任何满足 f ( S ) > 4 f(S) > 4 f(S)>4 S S S,答案是 4 4 4

在第二个例子中, S = { [ 1 , 9 ] } S=\{[1, 9]\} S={[1,9]}是唯一能满足 f ( S ) = 9 f(S)=9 f(S)=9的集合。由于每个可能的 S S S都满足 f ( S ) ≤ 9 f(S) \leq 9 f(S)9,所以答案是 9 9 9

在第三个例子中, S S S只能是一个空集,所以答案是 0 0 0

思路:

定义 d p [ i ] dp[i] dp[i]为前 i i i个位置选出的线段长度之和的最大值, s u m sum sum为前缀和数组,那么转移方程为
d p [ i ] = m a x j < i s u m [ i ] − s u m [ j ] ≥ 0 { d p [ j ] + i − j , d p [ i − 1 ] } dp[i]=\mathop{max}\limits_{\substack{j<i\\sum[i]-sum[j]\ge0}} \{dp[j]+i-j,dp[i-1]\} dp[i]=j<isum[i]sum[j]0max{dp[j]+ij,dp[i1]}
显然我们需要用线段树来优化dp,线段树记录每个位置的dp值减去自身下标后的值,即 d p [ j ] − j dp[j]-j dp[j]j,线段树维护最大值,且为权值线段树,每个点对应前缀和数组的值,根据 a i a_i ai的值可知需要对数据离散化进行线段树的下标映射,每次枚举 i i i点转移时,查询 ≤ s u m [ i ] \le sum[i] sum[i]的区间中最大值即可。

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<map>
#include<set>
#include<functional>
#include<unordered_set>
#include<queue>
#include<stack>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5, inf = (1 << 30);
int dp[N];  //dp[i]表示遍历到第i位时满足条件的最大区间总长
// 从数组a中选择若干个子数组,满足每个子数组的和分别>=0,
// 并且使得所有子数组的长度加起来尽可能的大
struct info {
    int maxval;
    info operator + (const info& t) const {
        info res;
        res.maxval = max(maxval, t.maxval);
        return res;
    }
};
struct Node {
    int l, r;
    info val;
}tr[4 * N];
void pushup(int u) {
    tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) {
        tr[u] = { l, l, -inf };
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}
void update(int u, int pos, int val) {
    if (tr[u].l == pos && tr[u].r == pos) {
        tr[u].val.maxval = max(tr[u].val.maxval, val);
        return;
    }
    int mid = (tr[u].l + tr[u].r) >> 1;
    if (pos <= mid) update(u << 1, pos, val);
    if (pos > mid) update(u << 1 | 1, pos, val);
    pushup(u);
}
info query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].val;
    int mid = (tr[u].l + tr[u].r) >> 1;
    if (l > mid) return query(u << 1 | 1, l, r);
    else if (r <= mid) return query(u << 1, l, r);
    else return query(u << 1, l, r) + query(u << 1 | 1, l, r);
}
void discretize(vector<ll>& pos, vector<ll>& a) {
    pos.assign(a.size(), 0);
    vector<ll> tmp = a;
    sort(tmp.begin() + 1, tmp.end());
    tmp.erase(unique(tmp.begin() + 1, tmp.end()), tmp.end());
    for (int i = 1; i <= a.size() - 1; i++) {
        pos[i] = lower_bound(tmp.begin() + 1, tmp.end(), a[i]) - tmp.begin();
    }
}
int main()
{
    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);
    int n;
    cin >> n;
    vector<ll> a(n + 1), sum(n + 1), pos;
    for (int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1] + a[i];
    build(1, 1, N);
    sum.push_back(0);
    discretize(pos, sum);
    update(1, pos[n + 1], 0);
    for (int i = 1; i <= n; i++) {
        dp[i] = i + query(1, 1, pos[i]).maxval;
        dp[i] = max(dp[i], dp[i - 1]);
        update(1, pos[i], dp[i] - i);
    }
    cout << dp[n];
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值