Codeforces Round 907 (Div. 2) ABCF

发布时间 2023-11-10 17:45:05作者: nannan4128

Codeforces Round 907 (Div. 2)ABCF

A. Sorting with Twos

题意:给你一个数组\(a_1,a_2,...,a_n\),你可以进行以下操作:

  • 选择一个非负整数\(m\),并且\(2^m\le n\)
  • \(1\le i \le 2^m\)的元素\(a_i\)减一

问:你能通过上述操作使得数组单调不减吗?

思路:对于不满足单调不减的位置,我们看他是不是\(2\)的幂次的位置,如果不是那就不行。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int a[N];
set<ll>mi;
void init(ll n)
{
    ll t = 1;
    for(int i = 0;i <= n; i++)
        mi.insert(t),t*=2;
}
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    init(1010);
    while(t--)
    {
        int n; cin>>n;
        for(int i = 1;i <= n; i++)
            cin>>a[i];
        bool ok = true;
        for(int i = 1;i < n; i++)
        {
            if(a[i]>a[i+1])
            {
                if(mi.find(i)==mi.end())
                {
                    ok = false;
                    break;
                }
            }
        }
        if(ok)
            cout<<"YES\n";
        else cout<<"NO\n";
    }


    return 0;
}

B. Deja Vu

题意:给你一个长度为\(n\)的数组\(a\),和\(q\)个次修改。

对于第\(i\)次修改,我们把所有能整除\(2^{x_i}\)次方的数加上\(2_{x_i-1}\),其中\((x_i\le30)\).问最终的数组是什么样的。

思路:一个数一定可以写成\(t\times 2^a\),如果\(a\ge x\)那么可以被整除。考虑\(2^a+2^{x-1} = 2^{x-1}\times(2^{a-(x-1)}+1)\)

其中:\(2^{a-(x-1)}+1\)是奇数,那么之后不可能在被\(2\)的幂次减小。那么我们可操作序列一定的一个单调递减序列。

因为\(x_i\le30\),那么最多操作\(30\)次,直接暴力即可。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll a[N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        int n,q; cin>>n>>q;
        for(int i = 1;i <= n; i++)
            cin>>a[i];

        int k = 100;
        while(q--)
        {
            int x; cin>>x;
            if(x>=k)continue;
            for(int i = 1;i <= n; i++)if(a[i]%(1<<x)==0){
                a[i]+=(1<<(x-1));
            }
            k = x;
        }

        for(int i = 1;i <= n; i++)
            cout<<a[i]<<" ";
        cout<<"\n";

    }
    return 0;
}

C. Smilo and Monsters

题意:有\(n\)个山洞,每个山洞有\(a_i\)只怪兽。现在要杀掉全部的怪兽。你有2种技能:

  • 第一种:选择一个山洞(至少有一个活着的怪兽),我们杀死他。组合技+1
  • 第二种:使用组合技,当山洞里面怪兽数论\(\ge x\),那么当前山洞怪兽数直接\(-x\),组合技\(x\)置为0.

思路:排序+贪心+双指针

因为一定是用小的累计,去大的里面用组合技是优的。注意特判\(l=r\)的情况。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int a[N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        int n; cin>>n;
        for(int i = 1;i <= n; i++)
            cin>>a[i];
        sort(a+1,a+1+n);
        ll ans = 0,x = 0;
        int l = 1,r = n;
        while(l<=r)
        {
           //cout<<"l = "<<l<<" r = "<<r<<"\n";
            if(l==r)
            {
                /*
                    d = a[l]-x

                    a[l] = 6, x = 2
                    d = 4
                    ans += d/2+1

                    a[l] = 6,x = 3
                    d = 3
                    ans += d/2 
                */
                if((a[l]-x)&1)
                {
                    ans+=(a[l]-x)/2;
                    if(x+(a[l]-x)/2>0)ans++;
                    ans++;
                }
                else
                {
                    ans+=(a[l]-x)/2+1;
                }
                break;
            }
            if(x<a[r])
            {
                int d = a[r]-x;
                //cout<<"d = "<<d<<"\n";
                if(a[l]<d)
                {
                    x += a[l];
                    ans += a[l];
                    a[l] = 0;
                    l++;
                }
                else if(a[l]==d)
                {
                    x += a[l];
                    ans += a[l];
                    a[l] = 0,a[r] = 0;
                    ans += 1,x = 0;
                    r--,l++;
                }
                else if(a[l]>d)
                {
                    a[l]-=d;
                    x += d;
                    ans += d;
                   // cout<<"d = "<<d<<"\n";
                    a[r] = 0;
                    r--,ans += 1,x = 0;
                }
            }
            else
            {
                x -= a[r];
                a[r] = 0;
                ans += 1;
                r--;
            }   
        }
        cout<<ans<<"\n";
    }


    return 0;
}
/*
13
17
13
5
10
12
14
*/

F. A Growing Tree

题意:给定一棵树,初始只含一个节点,编号为 \(1\) ,初始权值为 \(0\) ,设树的大小为 \(sz\)

\(q\) 次操作:

  • \(1\ \ x\) ,在 \(s\) 下挂一个节点,编号为 \(sz+1\) ,初始权值为 \(0\)

  • \(2\ \ x\ v\) ,将当前 \(x\) 子树中节点的权值加上 \(v\)

求所有操作完成后每个节点的点权。

思路:离线+倒序++dfs序+树状数组

对于子树的操作很容易想到转为\(dfs\)序然后区间操作。但是考虑到是动态生成的树,那咋办捏?我们考虑离线倒着做。我们先建立出最终的树,然后对于加一个点的操作,我们就记录这个点的答案,因为这就是它的最终答案,在此之前它还不存在,那么操作也没有影响。所以我们只需要正常加就好,因为是倒着的,之前的也不会影响之后的。记得最后算1(root)的答案。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 5e5 + 10;
ll  w[N];
vector<int> e[N];
int n, root, q;
int tot, l[N], r[N], tid[N], top[N], bs[N], sz[N], f[N], dep[N];

void dfs1(int u, int fa)
{
    sz[u] = 1;
    dep[u] = dep[fa] + 1;
    bs[u] = -1;
    f[u] = fa;
    for(auto v : e[u])
    {
        if(v == fa) continue;
        dfs1(v, u);
        sz[u] +=sz[v];
        if(bs[u] == -1 || sz[v] > sz[bs[u]])
            bs[u] = v;
    }
}

void dfs2(int u,int t)
{
    l[u] = ++tot;
    tid[tot] = u;
    top[u] = t;
    if(bs[u] != -1)
        dfs2(bs[u], t);
    for(auto v : e[u])
    {
        if(v == bs[u] || v == f[u])    continue;
        dfs2(v, v);
    }
    r[u] = tot;
}

template<class T>
struct BIT {
    T c[N];
    int size;
    void init()
    {
        for(int i = 0;i <= size; i++)
            c[i] = 0;
    }
    void resize(int s) { size = s;}
    T query(int x) { // 1 ... x
        assert(x <= size);
        T s = 0;
        for (; x; x -= x & (-x)) {
        s += c[x];
    }
     return s;
    }

    void modify(int x, T s) { // a[x] += s
        assert(x != 0);
        for (; x <= size; x += x & (-x)) {
            c[x] += s;
        }
    } 

     void change(int l, int r, ll val) {
        modify(l, val);
        modify(r + 1, -val); 
    }
};

BIT<ll>tr;


void solve()
{
    tot = 0;
    vector<array<int,3>>evt;
    cin>>q;
    for(int i = 1;i <= q; i++)
        e[i].clear();
    int n = 1;
    for(int i = 1;i <= q; i++)
    {
        int op; cin>>op;
        if(op==1)
        {
            int u; cin>>u;
            e[u].push_back(n+1);
            e[n+1].push_back(u);
            n++;
            evt.push_back({op,u,n});
        }else{
            int u,x; cin>>u>>x;
            evt.push_back({op,u,x});
        }
    }
    tr.init();
    tr.resize(n+10);
    root = 1;
    dfs1(root, 0);
    dfs2(root, root);

    reverse(evt.begin(),evt.end());
    int sz = n;
    //新增一个节点,我们查询它的值就是它最终的了,因为再往前他不存在
    for(auto [op,u,x] : evt)
    {
        if(op==1)
            w[x] = tr.query(l[x]);
        else
            tr.change(l[u],r[u],x);
    }
    w[1] = tr.query(l[1]);
    for(int i = 1;i <= n; i++)
        cout<<w[i]<<" ";
    cout<<"\n";
}

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


    return 0;
}