[牛客2020第4场]牛半仙的妹子序列

2024-03-16 23:18
文章标签 2020 牛客 序列 妹子 半仙

本文主要是介绍[牛客2020第4场]牛半仙的妹子序列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

牛半仙的妹子序列

题解

《关于我因过于这道题卡常而将其称为"贞卡常"的这件事》

场上被卡常了,下来加了几个优化就卡过去了。

这道题应该很容易看出是一个dp,40pts的 O ( n 2 ) O(n^2) O(n2)的dp式子应该是很好想的。
我们定义 d p i dp_{i} dpi为在只关注第 i i i个数到第 n n n个数之间的序列构成合法序列的方案数。
容易得到方程式

d p i = ∑ j = i + 1 n [ 满 足 条 件 ] d p j dp_{i}=\sum_{j=i+1}^{n}[满足条件]dp_{j} dpi=j=i+1n[]dpj

而答案就是我们的 d p 0 dp_{0} dp0

这种方式的dp是明显可以进行优化的,我们先考虑 d p i dp_{i} dpi对那些值产生了贡献。
容易发现, d p i dp_{i} dpi产生贡献的 d p j dp_{j} dpj满足条件
[ j < i ∧ v j < v i ∧ m a x k = j + 1 i { d p k < d p j } ] [j<i \wedge v_{j}<v_{i} \wedge max_{k=j+1}^{i}\{dp_{k}<dp_{j}\}] [j<ivj<vimaxk=j+1i{dpk<dpj}]
而这个条件我们需要想办法对其进行维护。
对于这个我们可以先按权值建一棵FHQ_Treap,每次进行操作时将值在 [ v i , n + 1 ) [v_{i},n+1) [vi,n+1)中的树给裂出去,在那棵值域小于 v i v_{i} vi的数加上 d p i dp_{i} dpi

但我们如何保证后半段条件呢?

我们可以对Treap上的每个点加上一个 v a l i val_{i} vali的值,表示 i i i号节点的坐标。而每次操作后都将操作的那个 d p i dp_{i} dpi对应的点从树中删掉。保证剩下的点都的 v a l val val值都小于删掉的点。加的过程中当且仅当所有的在点 j j j右边的点(即在原序列中 v v v值比它大的点)的 v a l val val值都小于它时才对其进行加dp值得操作。
由于这个 l z y lzy lzy值得加操作比较复杂,可能会被卡到 l o g n log_{n} logn,总时间复杂度 O ( n l o g n 2 ) O(nlog^2_{n}) O(nlogn2)存在着被卡掉的风险,所以我们需要对其加几个优化。

我们用一个二元组 ( x i , y i ) (x_{i},y_{i}) (xi,yi)表示 l z y i lzy_{i} lzyi
其中 x i x_{i} xi表示当前懒标记加值得大小,而 y i y_{i} yi表示需要点的 v a l val val值大于多少时才能加上当前的 l z y lzy lzy值。
我们知道,当前点如果可以加上 x i x_{i} xi的话,必定满足 v a l i val_{i} vali大于其右边子树的最大值和 y i y_{i} yi
1.如果当前子树的整体都小于 y i y_{i} yi的话就回退。
2.如果当前 y i y_{i} yi等于新加的 l z y lzy lzy的值的话就不用下传。
其实这两个剪枝再实际操作中都很有作用,可以证明我们最后的时间复杂度是可以过这道题的。
还有一些细枝末节的卡常技巧这里不做阐释。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define MAXN 200005
typedef long long LL;
const int mo=998244353;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){_T f=1;x=0;char s=getchar();while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}x*=f;
}
int n,a[MAXN],ad[MAXN];
int add(const int x,const int y){return x+y<mo?x+y:x+y-mo;}
int Max(const int x,const int y){return x>y?x:y;}
class FHQ_Treap{private:int ch[MAXN][2],rnd[MAXN],val[MAXN];int sum[MAXN],maxx[MAXN];pii lzy[MAXN];int siz[MAXN],tot,root;int newnode(const int v){int x=++tot;siz[x]=1;rnd[x]=rand();maxx[x]=val[x]=v;return x;}inline void updata(const int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;maxx[x]=val[x];if(ch[x][0])maxx[x]=Max(maxx[x],maxx[ch[x][0]]);if(ch[x][1])maxx[x]=Max(maxx[x],maxx[ch[x][1]]);}void downdata(const int x){if(!lzy[x].first)return ;if(maxx[x]<lzy[x].second){lzy[x]=make_pair(0,0);return ;}const int tm2=Max(maxx[ch[x][1]],lzy[x].second-1);if(tm2<val[x])sum[x]=add(sum[x],lzy[x].first);if(ch[x][1]){if(lzy[ch[x][1]].second==lzy[x].second)lzy[ch[x][1]].first=add(lzy[ch[x][1]].first,lzy[x].first);else downdata(ch[x][1]),lzy[ch[x][1]]=lzy[x];}const int tm1=Max(val[x],maxx[ch[x][1]]);if(ch[x][0]&&maxx[ch[x][0]]>tm1){const int tmp=Max(lzy[x].second,tm1);if(lzy[ch[x][0]].second==tmp)lzy[ch[x][0]].first=add(lzy[ch[x][0]].first,lzy[x].first);else downdata(ch[x][0]),lzy[ch[x][0]]=make_pair(lzy[x].first,tmp);}lzy[x]=make_pair(0,0);}int merge(const int a,const int b){if(!a||!b)return a+b;downdata(a);downdata(b);if(rnd[a]<rnd[b]){ch[a][1]=merge(ch[a][1],b);updata(a);return a;}ch[b][0]=merge(a,ch[b][0]);updata(b);return b;}void split(const int now,const int k,int &x,int &y){if(!now){x=y=0;return ;}downdata(now);if(k<a[val[now]])y=now,split(ch[now][0],k,x,ch[now][0]);else if(k==a[val[now]])x=now,y=ch[now][1],ch[now][1]=0;else x=now,split(ch[now][1],k,ch[now][1],y);updata(now);}void build(int &rt,const int l,const int r){if(l>r)return ;int mid=l+r>>1;rt=newnode(ad[mid]);build(ch[rt][0],l,mid-1);build(ch[rt][1],mid+1,r);updata(rt);}public:void Build(){maxx[0]=-1;build(root,0,n);lzy[root]=make_pair(1,0);}inline void query(const int v){int x1,y1,x2,y2;split(root,v-1,x1,y1);split(y1,v,x2,y2);downdata(x1);downdata(x2);lzy[x1]=make_pair(sum[x2],0);root=merge(x1,y2);}int ask(){downdata(root);return sum[root];}
}Tree;
signed main(){srand(191919);read(n);for(int i=1;i<=n;++i)read(a[i]),ad[a[i]]=i;Tree.Build();for(int i=n;i;--i)Tree.query(a[i]);printf("%d\n",Tree.ask());return 0;
}

谢 谢 \color{red}{谢谢}

这篇关于[牛客2020第4场]牛半仙的妹子序列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/817056

相关文章

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

POJ1631最长单调递增子序列

最长单调递增子序列 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;import java.util.StringTokenizer;publ

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

day-50 求出最长好子序列 I

思路 二维dp,dp[i][h]表示nums[i] 结尾,且有不超过 h 个下标满足条件的最长好子序列的长度(0<=h<=k),二维数组dp初始值全为1 解题过程 状态转换方程: 1.nums[i]==nums[j],dp[i,h]=Math.max(dp[i,h],dp[j,h]+1) 2.nums[i]!=nums[j],dp[i,h]=Math.max(dp[i,h],dp[j,h-1

LeetCode:3177. 求出最长好子序列 II 哈希表+动态规划实现n*k时间复杂度

3177. 求出最长好子序列 II 题目链接 题目描述 给你一个整数数组 nums 和一个非负整数k 。如果一个整数序列 seq 满足在下标范围 [0, seq.length - 2] 中 最多只有 k 个下标i满足 seq[i] != seq[i + 1] ,那么我们称这个整数序列为好序列。请你返回 nums中好子序列的最长长度。 实例1: 输入:nums = [1,2,1,1,3],

用Python实现时间序列模型实战——Day 14: 向量自回归模型 (VAR) 与向量误差修正模型 (VECM)

一、学习内容 1. 向量自回归模型 (VAR) 的基本概念与应用 向量自回归模型 (VAR) 是多元时间序列分析中的一种模型,用于捕捉多个变量之间的相互依赖关系。与单变量自回归模型不同,VAR 模型将多个时间序列作为向量输入,同时对这些变量进行回归分析。 VAR 模型的一般形式为: 其中: ​ 是时间  的变量向量。 是常数向量。​ 是每个时间滞后的回归系数矩阵。​ 是误差项向量,假

时间序列|change point detection

change point detection 被称为变点检测,其基本定义是在一个序列或过程中,当某个统计特性(分布类型、分布参数)在某时间点受系统性因素而非偶然因素影响发生变化,我们就称该时间点为变点。变点识别即利用统计量或统计方法或机器学习方法将该变点位置估计出来。 Change Point Detection的类型 online 指连续观察某一随机过程,监测到变点时停止检验,不运用到

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>