Educational Codeforces Round 67小结

已AC:C,D,E,F

很喜欢这场比赛的题目风格。而且也不难。

C.Vasya And Array

题目大意:

给你 m m m条两类限制:区间 [ L , R ] [L,R] [L,R]单调非降 / 区间 [ L , R ] [L,R] [L,R]不符合单调非降.
问你是否能够找到一种符合所有限制的长度为 n n n的序列 a i a_i ai

n , m ≤ 1000 n,m \leq 1000 n,m1000

题目思路:思维,贪心

我们考虑一个位置一个位置的填。然后从前往后考虑每对相邻的数 ( i , i + 1 ) (i,i+1) (i,i+1).如果他们被完整的被包含在第一种条件限制中,那么这两个数必须要增。否则我们让他递减(贪心).因为我们是先满足条件1的限制,之后再暴力 c h e c k check check条件二.所以在满足条件1的情况下,我们想让序列尽量不增,这样好满足条件2.
想到这一点这题就很好做了. O ( n 2 ) O(n^2) O(n2)暴力搞即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vl vector<ll>
const int maxn = 1000 + 5;
const int mod = 1e9 + 7;
int a[maxn];
struct No{
    
    int l , r;};
vector<No> t[2];
bool check (int l , int r)
{
    
    
    for (auto g : t[0])
        if (g.l <= l && r <= g.r) return true;
    return false;
}
int main()
{
    
    
    ios::sync_with_stdio(false);
    int n , m; cin >> n >> m;
    for (int i = 1 ; i <= m ; i++){
    
    
        int x , y , z; cin >> x >> y >> z;
        x = !x;
        t[x].pb({
    
    y , z});
    }
    a[1] = 1005;
    for (int i = 2 ; i <= n ; i++){
    
    
        if (check(i - 1 , i)) a[i] = a[i - 1] + 1;
        else a[i] = a[i - 1] - 1;
    }
    bool ok = true;
    for (auto g : t[1]){
    
    
        bool res = true;
        for (int i = g.l ; i < g.r && res; i++){
    
    
            if (a[i] > a[i + 1]) res = false;
        }
        if (res){
    
    
            ok = false;
            break;
        }
    }
    if (!ok){
    
    
        cout << "NO" << endl;
        return 0;
    }
    cout << "YES" << endl;
    for (int i = 1 ; i <= n ; i++){
    
    
        cout << a[i] << " ";
    }
    cout << endl;
    return 0;
}
进一步优化?

暴力check的部分我们用前缀最大值优化,复杂度降为 O ( n ) O(n) O(n).

D. Subarray Sorting

题目大意:

给你两个数组 a , b a,b a,b.你每次可以取 a a a的一个子数组,对其进行排序。问你是否能够将其转化为 b b b.

题目思路:

1.先判排序后 a = = b a==b a==b.
2.由于 b b b的顺序不一定是严格升序。而且一旦对一段排完序之后 大的就不能移动到小的之前 了。所以我们尽量对相邻两个数之间排序。例如:
a = [7,6,5]
b = [5,7,6]
3.由于要每一个位置上的数相同。那么我们简单的考虑从左往右一个一个匹配。循环b数组 1 到 i 1到i 1i. p t r ptr ptr维护当前匹配到的 a a a数组的位置.

我们需要知道每个数的从小到大的位置。这个对值域开队列即可。对于 b i b_i bi,我们在 a a a找到离 i i i最近的位置 r r r.那么这个数能够被挪动到位置 i i i的条件是: [ i , r ] [i,r] [i,r]内最小值 = b i = b_i =bi.这个我们用线段树维护区间最小值.

挪动完之后,我们将 a r : = 1 e 9 a_r:=1e9 ar:=1e9。代表废除这个位置。 p t r ptr ptr不变.

启示:

面对这种需要多次挪动数组的某个数到最前面的问题,不用真的移动,而是用一个指针维护当前数组位置+将这个数改成 i n f inf inf(一个无效的数)来模拟这个问题。

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vl vector<ll>
#define tl (t << 1)
#define tr (t << 1 | 1)
#define mid ((l + r) >> 1)
const int maxn = 3e5 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
int a[maxn] , b[maxn] , n;
int mi[maxn << 2];
void pushup(int t)
{
    
    
    mi[t] = min(mi[tl] , mi[tr]);
}
void build (int t , int l , int r)
{
    
    
    if (l == r) {
    
    
        mi[t] = a[l];
        return ;
    }
    build(tl , l , mid);
    build(tr , mid + 1 , r);
    pushup(t);
    return ;
}
int ask (int t , int l , int r , int L , int R)
{
    
    
    if (L <= l && r <= R) return mi[t];
    int ans = 1e9;
    if (L <= mid) ans = min(ans , ask(tl , l , mid , L , R));
    if (R > mid) ans = min(ans , ask(tr , mid + 1 , r , L , R));
    return ans;
}
void modify (int t , int l , int r , int p , int c)
{
    
    
    if (l == r){
    
    
        mi[t] = c;
        return ;
    }
    if (p <= mid) modify(tl , l , mid , p , c);
    else modify(tr , mid + 1 , r , p , c);
    pushup(t);
    return ;
}

int tmp[2][maxn];
bool check ()
{
    
    
    for (int i = 1 ; i <= n ; i++) tmp[0][i] = a[i];
    for (int i = 1 ; i <= n ; i++) tmp[1][i] = b[i];
    sort(tmp[0] + 1 , tmp[0] + 1 + n);
    sort(tmp[1] + 1 , tmp[1] + 1 + n);
    for (int i = 1 ; i <= n ; i++) if (tmp[0][i] != tmp[1][i]) return false;
    return true;
}

int main()
{
    
    
    ios::sync_with_stdio(false);
    int t; cin >> t;
    while (t--){
    
    
        cin >> n;
        set<int> s[n + 2];
        for (int i = 1 ; i <= n ; i++)
            cin >> a[i] , s[a[i]].insert(i);
        for (int i = 1 ; i <= n ; i++)
            cin >> b[i];
        if (!check()){
    
    
            cout << "NO" << endl;
            continue;
        }
        build(1 , 1 , n);
        bool ok = true;
        int ptr = 1;
        for (int i = 1 ; i <= n ; i++){
    
    
            while (a[ptr] == inf) ptr++;
            int now = a[ptr];
            if (b[i] == now) {
    
    
                s[now].erase(ptr);
                ptr++;
                continue;
            }
            int r = *s[b[i]].begin();
            if (ask(1 , 1 , n , ptr , r) != b[i]){
    
    
                ok = false;
                break;
            }
            modify(1 , 1 , n , r , inf);
            a[r] = inf;
            s[b[i]].erase(r);
        }
        cout << (ok ? "YES" : "NO") << endl;
    }
    return 0;
}
*/

E - Tree Painting,水题

题目大意:

让你确定一个根,使得各点深度之和相加最大.

题目思路:

换根计算即可.

F. Expected Square Beauty - 计数,期望

见:下一篇博客

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/113973776