刷题之Codeforces Round #731 (Div. 3)

[1547A]. Shortest Path with Obstacle

  • 思路: F F F只有在 A , B A,B A,B两位置与坐标平行的直线距离之间时才会影响(距离 + 2 +2 +2

    #include<bits/stdc++.h>
    #define fora(i, a, b) for(int i = a; i <= b; i++)
    #define fors(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    using namespace std;
    int t, xa, ya, xb, yb, xf, yf, ans;
    int main(){
        cin >> t;
        while(t--){
            cin >> xa >> ya >> xb >> yb >> xf >> yf;
            ans = abs(xa - xb) + abs(ya - yb);
            if(xa == xb && xa == xf && min(ya, yb) < yf && yf < max(ya, yb)) ans += 2;
            if(ya == yb && ya == yf && min(xa, xb) < xf && xf < max(xa, xb)) ans += 2;
            cout << ans << endl;
        }
    }
    

[1547B]. Alphabetical Strings

  • 思路:首先要判断没有重复,其次要判断按顺序出现(小的没出大的不能有)。最后查看能否成立,对于任意两个 j ,   i j,\,i j,i,以 a a a为中心原点有 ∣ j ∣ < ∣ i ∣ |j|<|i| j<i时,只要保证 a [ j ] ≤ a [ i ] a[j]\le a[i] a[j]a[i]即可

    #include<bits/stdc++.h>
    #define fora(i, a, b) for(int i = a; i <= b; i++)
    #define fors(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    using namespace std;
    int t, a[30], ls, no, v, flg[30];
    string s;
    int main(){
        cin >> t;
        while(t--){
            cin >> s;
            ls = s.length(), no = 0;
            memset(flg, 0, sizeof(flg));
            memset(a, 0, sizeof(a));
            fora(i, 0, ls - 1){
                if(flg[s[i] - 'a' + 1]) no = 1;  //判断重复
                else flg[s[i] - 'a' + 1] = 1;
                if(s[i] == 'a') v = i;
            }
            fora(i, 1, ls) if(!flg[i]) no = 1;  //要求按顺序出现
            fora(i, 0, ls - 1) a[s[i] - 'a' + 1] = i - v;
            fora(i, 1, ls)
                fora(j, 1, i - 1) if((0 < a[i] && a[i] < a[j]) || (a[i] < 0 && a[j] < a[i])) no = 1;
            cout << (no == 1 ? "NO" : "YES") << endl;
        }
    }
    

[1547C]. Pair Programming

  • 思路:只有当选择数字大于 0 0 0时才有可能由于行数不够而出错,因此为了尽量在输入数字之前使更多的 0 0 0输入,且要保证按照 a , b a,b a,b数组的顺序输出,可以每次选择两个数组第一待选择中较小的那个数,最后再将还剩余的数组选入补齐。接着只需要判断数字大于 0 0 0时行数是否足够即可

    #include<bits/stdc++.h>
    #define fora(i, a, b) for(int i = a; i <= b; i++)
    #define fors(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    using namespace std;
    int t, k, n, m, x, y, a[105], b[105], no;
    int main(){
        cin >> t;
        while(t--){
            cin >> k >> n >> m;
            queue<int> q, p;
            no = 0, x = 0, y = 0;
            fora(i, 1, n) cin >> a[i];
            fora(i, 1, m) cin >> b[i];
            while(x < n && y < m){
                if(a[x + 1] <= b[y + 1]) q.push(a[++x]);  //输入较小的
                else q.push(b[++y]);
            }
            while(x < n) q.push(a[++x]);  //补齐
            while(y < m) q.push(b[++y]);  //补齐
            while(q.size()){
                int tmp = q.front(); q.pop();
                if(tmp > k) no = 1;  //判断行数是否足够
                if(tmp == 0) k++;
                if(no != 1) p.push(tmp);
            }
            if(no == 1) cout << -1 << endl;
            else{
                while(p.size()){
                    cout << p.front() << " ";
                    p.pop();
                }
                cout << endl;
            }
        }
    }
    

[1547D]. Co-growing Sequence

  • 思路:首先由式子 a i & a i + 1 = a i a_i\&a_{i+1}=a_i ai&ai+1=ai,对于每一位 a i + 1 a_{i+1} ai+1有( a i a_i ai 1 1 1 a i + 1 a_{i+1} ai+1必为 1 1 1

    a i a_i ai0011
    a i + 1 a_{i+1} ai+10111

    ​ 记 a i − 1 = x i ⊕ y i a_{i-1} = x_i\oplus y_i ai1=xiyi a i − 1 a_{i-1} ai1 p p p位等于 1 1 1时: a i a_i ai p p p位等于 1 1 1,则 x i = 1 x_i=1 xi=1 y i = 0 y_i=0 yi=0 x i = 0 x_i=0 xi=0 y i = 1 y_i=1 yi=1;当 a i − 1 a_{i-1} ai1 p p p位等于 0 0 0时: a i a_i ai p p p位无限制,则 y i = 0 y_i=0 yi=0为理想情况(使字典序更小)。

    a i − 1 a_{i-1} ai10011
    a i a_i ai0111
    x i x_i xi1010
    y i y_i yi1001

    故可以算出 y i = ( a i − 1 ∣ x i ) ⊕ x i y_i=(a_{i-1}|x_i)\oplus x_i yi=(ai1xi)xi

    #include<bits/stdc++.h>
    #define fora(i, a, b) for(int i = a; i <= b; i++)
    #define fors(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    const int maxn = 2e5 + 5;
    using namespace std;
    int t, n;
    ll x[maxn], y[maxn];
    int main(){
        cin >> t;
        while(t--){
            cin >> n;
            fora(i, 1, n) scanf("%d", &x[i]);
            memset(y, 0, sizeof(y));
            cout << y[1] << " ";
            fora(i, 2, n){
                y[i] = ((y[i - 1] ^ x[i - 1]) | x[i]) ^ x[i];
                cout << y[i] << " ";
            }
            cout << endl;
        }
    }
    

[1547E]. Air Conditioners

  • 思路:算是动态规划,对于每个屋子,先假设每个空调屋只对其后面的屋子有影响 d p [ i ] = m i n ( d p [ i − 1 ] + 1 , t [ i ] ) dp[i] = min(dp[i - 1] + 1, t[i]) dp[i]=min(dp[i1]+1,t[i]),从最早的空调屋正向遍历一遍;再检查每个空调屋对其前面屋子的能否影响,使 d p [ i ] = m i n ( d p [ i ] , d p [ i + 1 ] + 1 ) dp[i] = min(dp[i], dp[i + 1] + 1) dp[i]=min(dp[i],dp[i+1]+1),反向遍历一遍。

    #include<bits/stdc++.h>
    #define fora(i, a, b) for(int i = a; i <= b; i++)
    #define fors(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    const int INF = 0x7fffffff;
    const int maxn = 3e5 + 5;
    using namespace std;
    int q, n, k;
    ll minn, a[maxn], t[maxn], dp[maxn];
    int main(){
        cin >> q;
        while(q--){
            cin >> n >> k;
            memset(t, 0, sizeof(t));
            memset(dp, 0, sizeof(dp));
            minn = INF;
            fora(i, 1, k){
                scanf("%d", &a[i]);
                minn = min(minn, a[i]);  //记录最早有空调的屋子
            }
            fora(i, 1, k) scanf("%d", &t[a[i]]);
            dp[minn] = t[minn];
            fora(i, minn + 1, n){  //正向遍历一遍,从最早有空调的屋子开始
                if(t[i] > 0) dp[i] = min(dp[i - 1] + 1, t[i]);
                else dp[i] = dp[i - 1] + 1;
            }
            fors(i, n - 1, 1){  //反向遍历一遍
                if(dp[i] > 0) dp[i] = min(dp[i], dp[i + 1] + 1);
                else dp[i] = dp[i + 1] + 1;
            }
            fora(i, 1, n) printf("%lld ", dp[i]);
            cout << endl;
        }
    }
    

[1547F]. Array Stabilization (GCD version)

  • 思路:发现第 k k k次操作时 b i = gcd ⁡ ( a i , a i + 1 , a i + 2 , . . . , a i + k ) b_i=\gcd(a_i,a_{i+1},a_{i+2},...,a_{i+k}) bi=gcd(ai,ai+1,ai+2,...,ai+k),而且最终操作结果使得数组所有数变为 c o m g c d comgcd comgcd(所有数的最大公约数)。

    ​ 于是找到以 a [ i ] a[i] a[i]为开头的最短区间 g c d gcd gcd使其等于 c o m g c d comgcd comgcd,则此区间长度 p p p即为第 i i i个数需要的步骤。由于 p p p是唯一确定的且 p ∈ [ 0 , n ] p\in [0,n] p[0,n]单调,所以可以用二分查找来求 p p p;在计算区间 g c d gcd gcd时可以用 S T ST ST使复杂度大幅降低。

  • 详细思路推荐这位大佬

  • 复杂度: O ( n log ⁡ ( n log ⁡ max ⁡ { a [ i ] } ) ) O(n\log(n\log\max\{a[i]\})) O(nlog(nlogmax{a[i]}))

    //复杂度O(n * log(n * log max(a[i])))
    #include<bits/stdc++.h>
    #define fora(i, a, b) for(int i = a; i <= b; i++)
    #define fors(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    const int INF = 0x7fffffff;
    const int maxn = 2e5 + 5;
    using namespace std;
    int t, n, a[2 * maxn], comgcd, st[2 * maxn][20], ans;
    int gcd(int a, int b){
        return  (b ? gcd(b, a % b) : a);
    }
    int main(){
        cin >> t;
        while(t--){
            memset(a, 0, sizeof(a));
            ans = 0;
            cin >> n;
            comgcd = -1;
            fora(i, 1, n){
                scanf("%d", &a[i]);
                a[i + n] = a[i];
                comgcd =  (comgcd == -1 ? a[i] : gcd(comgcd, a[i]));
            }
            int equ = 1;
            fora(i, 1, n - 1) if(a[i] != a[i + 1]) equ = 0;
            if(equ == 1){
                cout << 0 << endl;
                continue;
            }
            fora(j, 0, 20)  //用st表快速计算区间gcd
                fora(i, 1, n + n + 1 - (1 << j)){  //n + n是将环变为二倍长度的链
                    if(j == 0) st[i][j] = a[i];
                    else st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
                }
            fora(i, 1, n){
                int l = i, r = i + n - 1;
                while(l < r){  //二分查找l使得从i到l的区间gcd == comgcd
                    int mid = (l + r) / 2, k = log2(mid - i + 1);
                    if(gcd(st[i][k], st[mid - (1 << k) + 1][k]) != comgcd) l = mid + 1;  //表示从i到mid的区间gcd都还不是最终答案
                    else r = mid;
                }
                ans = max(ans, l - i);
            }
            cout << ans << endl;
        }
    }
    

[1547G]. How Many Paths?

变强了再做

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值