首页 > 其他分享 >NZOJ NOIP模拟赛1

NZOJ NOIP模拟赛1

时间:2024-10-31 21:31:57浏览次数:1  
标签:NOIP int ne ctz ai NZOJ dp 排序 模拟

T1 好数

设ctz(x)为x二进制下末尾0的个数,如ctz(1001000)=3。
设ppc(x)为x二进制下1的个数,如ppc(1001000)=2。
定义一个数是好数,当且仅当ctz(x)=ppc(x)。
给定Q,有Q次询问,每次给出区间[l,r],你需要求出[l,r]中任意一个好数,或判断无输出-1。

考虑逐位模拟,我们从大到小考虑,如果 \(x\) 非法时如何操作。

  • \(\operatorname{ctz}(x) > \operatorname{ppc}(x)\) 时,我们将 \(x\) 减去 \(1\),再去考虑新的数。

  • \(\operatorname{ctz}(x) < \operatorname{ppc}(x)\) 时,我们将 \(x\) 减去 \(\operatorname{lowbit}(x)\),此时一定是一个最接近 \(x\) 的可能的合法操作,减少一个 \(1\) 的同时增加一个 \(0\),因为对于 \((x -\operatorname{lowbit}(x), x]\) 中一定不存在合法的 \(x\),否则 \(1\) 比末尾 \(0\) 多。

时间复杂度 \(O(Q\log{n} \sim Q\log^2{n})\),但上界是远远达不到的。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;

int ctz(ull x)
{
    x = x & -x;
    int cnt = 0;
    while (x) x >>= 1, cnt ++ ;
    return cnt - 1;
}

void solve()
{
    ull l, r;
    cin >> l >> r;
    while (r >= l)
    {
        if (__builtin_popcount(r) < ctz(r)) r -- ;
        else if (__builtin_popcount(r) > ctz(r)) r -= r & -r;
        else return cout << r << "\n", void();
    }
    cout << "-1\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
    cin >> T;
    while (T -- ) solve();
    return 0;
}

T2 排序

今天是YQH的生日,她得到了一个长度为n排列a1,a2,...,an 作为生日礼物,这 个排列包含了 1,2,...,n 里每个元素恰好一次。
由于YQH十分喜欢升序,于是她打算把这个排列进行排序使得最后排列升序。
但是她发现一个问题:她太笨了,不会各种O(nlogn)的排序算法,于是她退而求次,准备使用O(n) 的“斯大林排序”。
斯大林排序是这样一个过程:
假设我们要排序的序列是a1,a2,...,an,那么维护一个初始为空的栈,然后从 1 到 n 枚 举i:
• 假如ai 比栈顶严格大或者栈为空,那么把ai加入栈中。
• 否则,此刻有ai小于等于栈顶,那么什么都不干。
最后把栈里的元素从底到顶输出作为结果。
容易发现,这样我们获得就是原排列的一个上升子序列,但是可能会失去太多的元 素,比如假如我们要排序[5,1,2,3,4],我们最后只得到了 [5]。
于是YQH进行了改进,现在排序变成这样一个过程:
假设我们要排序的序列是a1,a2,...,an,那么维护一个初始为空的栈,然后从 1 到 n 枚举 i:
• 假如 ai 比栈顶严格大或者栈为空,那么把ai加入栈中。
• 否则,此刻有 ai 小于等于栈顶,那么有两种选择:把栈顶弹出并把ai加入栈中, 或什么都不干。
注意,你需要保证任意时刻,栈中的元素从底到顶严格单调上升。
最后把栈里的元素从底到顶输出作为结果。
比如现在我们要排序[5,1,2,3,4],栈的变化是 [] → [5] → [1] → [1,2] → [1,2,3] → [1,2,3,4]。
容易发现,最后栈的大小与排序时进行的选择有关。YQH当然希望最后栈越大越 好,于是她找到了你,希望你对于她给出的排列,求出最后栈大小的最大值。

容易发现,对于一个位置的 \(a_i\),所有后续连续的 \(a_j < a_i(j > i)\),我们是可以进行删除或替换当前 \(a_i\) 操作的,记 \(ne_i = \min(\{j \mid a_j > a_i, j > i\})\),但 \(dp_i\) 对 \(a_j > a_i, j \in [ne_i, ne_{ne_i})\) 的 \(dp_j\) 均有贡献,合法的 \(j\) 是离散的,这并不好办到,因此我们考虑从值域入手。

设 \(dp_i\) 表示以 \(i\) 结尾的栈并且栈不弹出 \(i\) 的最大长度,从 \(1 \sim n\) 枚举考虑能否对答案进行贡献,由上面的定义可知,对 \(i\) 的合法转移是删除 \(i\) 后比 \(i\) 小区间段,即 \(dp_j = \max(dp_j, dp_i + 1), j \in [ne_i, ne_{ne_i})\),我们用线段树对区间段进行赋值即可。

时间复杂度 \(O(n\log{n})\)。

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n;
int a[N], p[N], ne[N], dp[N];
struct Node
{
    int l, r, sum;
}tr[N << 2];

void build(int u, int l, int r)
{
    tr[u] = {l, r, -100000};
    if (l == r) return;
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

void modify(int u, int l, int r, int d)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum = max(tr[u].sum, d), void();
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify(u << 1, l, r, d);
    if (r > mid) modify(u << 1 | 1, l, r, d);
}

int query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
    int mid = tr[u].l + tr[u].r >> 1;
    int res = tr[u].sum;
    if (l <= mid) res = max(res, query(u << 1, l, r));
    if (r > mid) res = max(res, query(u << 1 | 1, l, r));
    return res;
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i], p[a[i]] = i;
    ne[n + 1] = a[n + 1] = n + 1;
    for (int i = n; i >= 0; i -- )
    {
        ne[i] = i + 1;
        while (a[ne[i]] < a[i]) ne[i] = ne[ne[i]];
    }
    build(1, 1, n);
    int ans = 0;
    for (int i = 0; i <= n; i ++ )
    {
        int u = p[i];
        int l = ne[u], r = ne[l] - 1;
        if (u) dp[u] = query(1, u, u) + 1;
        if (l <= r) modify(1, l, r, dp[u]);
        ans = max(ans, dp[u]);
    }
    cout << ans << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T = 1;
    while (T -- ) solve();
    return 0;
}

标签:NOIP,int,ne,ctz,ai,NZOJ,dp,排序,模拟
From: https://www.cnblogs.com/YipChipqwq/p/18518938

相关文章

  • [NOIP2008 提高组] 笨小猴——map的应用
    题目描述笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!这种方法的具体描述如下:假设\(\text{maxn}\)是单词中出现次数最多的字母的出现次数,\(\text{minn}\)是单词中出现次数最少的字母的......
  • CSP-S 2022 - 模拟赛记录
    PrefaceT1调的太久了,应当先打够部分分就切题的,全分思维难度不高,代码难度超高。可能是出题人知道把最简单题放T2有点过于恶心,所以后两道题的部分分都很好打,给的分也很多,一共\(55\)分可以轻松到手。就是第二题卡了一个unsignedlonglong,有点莫名其妙,而且T1放模拟也是头......
  • 「模拟赛」多校 A 层联训 15
    比赛链接A.追逐游戏(chase)没啥意义的水题,但赛时没调出来。分讨,LCA设\(S\)和\(T\)的LCA为\(lca\)\(S'\)为\(lca\)的祖先节点的时候,\(S'\)到达\(s->T\)这条链上的第一个点\(x\)一定是\(lca\)否则,用LCA求出来\(S'\)到\(s->T\)这条链上的第一个点......
  • 多校 A 层冲刺 NOIP2024 模拟赛 16
    多校A层冲刺NOIP2024模拟赛16T1四舍五入签到题注意到一个数是否会入上去只和其剩余系有关,即满足\(i\modj<\frac{1}{2}j\),会入上去,考虑枚举j的倍数,其贡献就成了一个区间,差分即可。时间复杂度是调和级数的,为\(O(n\lnn)\)。T2填算符贪心,特殊性质显然将所有\(\&\)放......
  • NOIP2024集训Day65 贪心
    NOIP2024集训Day65贪心A.[NOI2015]荷马史诗简化题意,即构造一颗\(k\)叉树,每个节点的权值为其所有孩子的权值之和,给定的\(n\)个数必须使用,其余空缺处用\(0\)补全。考虑使用优先队列,首先弹入\(n+(n-1)\%(k-1)\)个元素(不足处用0代替),然后每次弹出前\(k\)小的数......
  • DVD管理系统 (连接数据库--项目模拟)
    本章主要是增加和查看功能,其他的删除和修改(借出/归还)只是写了工具类和接口DVD类属性----必须与数据库里面,我们所调用的表一一对应!!!!packagedvd.entry;/***实体类---一对一参照表*表名=类名(首字母大写)*字段名===属性名*字段类型==属性类型*/publicclas......
  • NOIP 模拟赛:2024-10-30
    T1:一场比赛一共有\(n\)位选手和\(m\)道题目,其中你是第\(1\)位选手。你现在知道了每位选手通过了哪些题目。你可以调整题目的顺序,然后给题目赋予一个分值,使得第\(i\)道题目的分值是\(2^i\)。你想知道能否通过调整题目的顺序,使得你的成绩恰好是第二高的。保证不存在两个选手的通......
  • NOIP 模拟赛 Day 1
    赛时很神秘,自己设定开始时间。开T1,发现了一些性质,但是对着题面盯了1h什么思路也没有。开T2,博弈论,打了个SG函数的表,发现是SG函数是\(a_i\bmod(j+1)\)这样子的,这样就有了\(O(n^2)\),拿到了\(30\)pts。此时2h。开T3,会了暴力枚举全排列,这个复杂度是\(O(n!)\)的,有......
  • 2024.10.31模拟赛
    一定要好好睡觉啊,不然打模拟赛的时候会困死的!!!非常非常困的7:50时就开始打模拟赛,还是打了四个小时。打了T1、T2的正解,T3的5分特殊样例、T3的10分特殊样例,预计总215分。然后经过漫长的三个小时的等待,出现了T1100分,T265分,T360分,T410分、总分235分的神奇成绩。虽然结果比预......
  • NOIP2015 提高组 子串
    NOIP2015提高组子串感觉是最长公共子序列模型的变式。容易想到记\(f[i][j][k]\)表示\(A\)走到了第\(i\)位,\(B\)匹配上了\(1\simj\),目前分成了\(k\)段的方案数。如果强制第\(i\)位必须匹配上的话,需要枚举位置\(p\),满足\(A[p]=B[j-1]\)。这样的复杂度是\(......