• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
nannandbk
博客园    首页    新随笔    联系   管理    订阅  订阅
Codeforces Round 907 (Div. 2) ABCF

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;
}

posted on 2023-11-10 17:38  nannandbk  阅读(74)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3