首页 > 其他分享 >题解 P7972【[KSN2021] Self Permutation】

题解 P7972【[KSN2021] Self Permutation】

时间:2023-11-17 09:00:10浏览次数:31  
标签:return lc int 题解 Self KSN2021 operator Modint friend

怎么其他两篇题解都是 \(O(n\log n)\) 的,来发一个 \(O(n)\) 做法,当考前复习了。

对原序列建出小根笛卡尔树,节点编号与原序列中的下标相同。记 \(T_u\) 表示以 \(u\) 为根的子树,\(lc(u),rc(u)\) 分别表示 \(u\) 的左儿子和右儿子。

设 \(f_u\) 表示删除若干 \(T_u\) 中的点(可以不删),但不能删空的方案数。

假设节点 \(u\) 没有删除,此时左右子树可以删空也可以不删空,有转移:

\[f_u\gets(f_{lc(u)}+1)(f_{rc(u)}+1) \]

节点 \(u\) 可以被删除,当且仅当其左侧存在比它小的数且它们中间所有数都被删了,或者其右侧存在比它小的数且它们中间所有数都被删了。根据笛卡尔树性质,假设 \(T_u\) 的下标范围是 \([l_u,r_u]\),则 \(l_u-1,r_u+1\) 两个下标的数如果存在,必定比 \(u\) 下标的数要小。根据这一点,有转移:

\[\begin{cases} f_u\gets f_{rc(u)}&\textrm{if }l_u > 1\\ f_u\gets f_{lc(u)}&\textrm{if }r_u > 1\\ \end{cases} \]

时间复杂度 \(O(n)\)。

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
    uniform_int_distribution<int> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

template<int mod>
inline unsigned int down(unsigned int x) {
    return x < mod ? x : x - mod;
}

template<int mod>
struct Modint {
    unsigned int x;
    Modint() = default;
    Modint(int x) : x(x) {}
    friend istream& operator>>(istream& in, Modint& a) {return in >> a.x;}
    friend ostream& operator<<(ostream& out, Modint a) {return out << a.x;}
    friend Modint operator+(Modint a, Modint b) {return down<mod>(a.x + b.x);}
    friend Modint operator-(Modint a, Modint b) {return down<mod>(a.x - b.x + mod);}
    friend Modint operator*(Modint a, Modint b) {return 1ULL * a.x * b.x % mod;}
    friend Modint operator^(Modint a, int k) {Modint ans = 1; for(; k; k >>= 1, a *= a) if(k & 1) ans *= a; return ans;}
    friend Modint operator~(Modint a) {return a ^ (mod - 2);}
    friend Modint operator/(Modint a, Modint b) {return a * ~b;}
    friend Modint& operator+=(Modint& a, Modint b) {return a = a + b;}
    friend Modint& operator-=(Modint& a, Modint b) {return a = a - b;}
    friend Modint& operator*=(Modint& a, Modint b) {return a = a * b;}
    friend Modint& operator^=(Modint& a, int k) {return a = a ^ k;}
    friend Modint& operator/=(Modint& a, Modint b) {return a = a / b;}
    friend Modint& operator++(Modint& a) {return a += 1;}
    friend Modint operator++(Modint& a, int) {Modint b = a; ++a; return b;}
    friend Modint& operator--(Modint& a) {return a -= 1;}
    friend Modint operator--(Modint& a, int) {Modint b = a; --a; return b;}
    friend Modint operator-(Modint a) {return a.x == 0 ? 0 : mod - a.x;}
    friend bool operator==(Modint a, Modint b) {return a.x == b.x;}
    friend bool operator!=(Modint a, Modint b) {return !(a == b);}
};

typedef Modint<1000000007> mint;

const int N = 3e5 + 5;

int n, a[N], lc[N], rc[N], stk[N], top;
mint dp[N];

void dfs(int u, int l, int r) {
    if(lc[u]) dfs(lc[u], l, u - 1);
    if(rc[u]) dfs(rc[u], u + 1, r);
    dp[u] += (dp[lc[u]] + 1) * (dp[rc[u]] + 1);
    if(l > 1) dp[u] += dp[rc[u]];
    if(r < n) dp[u] += dp[lc[u]];
} 

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> n;
    rep(i, 1, n) cin >> a[i];
    stk[++top] = 1;
    rep(i, 2, n) {
        while(top && a[stk[top]] > a[i]) --top;
        if(!top) lc[i] = stk[1];
        else {
            lc[i] = rc[stk[top]];
            rc[stk[top]] = i;
        }
        stk[++top] = i;
    }
    int rt = min_element(a + 1, a + 1 + n) - a;
    dfs(rt, 1, n);
    cout << dp[rt] << endl;
    return 0;
}

标签:return,lc,int,题解,Self,KSN2021,operator,Modint,friend
From: https://www.cnblogs.com/ruierqwq/p/LG-P7972.html

相关文章

  • [ARC106F] Figures 题解
    题意给定\(N\)个带有若干洞的节点,其中第\(i\)个点上有\(d_i\)个洞。先可以在两个不同的节点的洞之间连边,一个洞最多连一条边,求使得最终形成的图是一棵树的方案数,对\(998244353\)取模。洞之间相互区分,两个方案不同当且仅当存在一条边在两个方案中的连的洞不同。\(2\l......
  • P9400 题解
    blog。很naive的题,写这篇题解,主要是现有题解都用的线段树/平衡树,让我感到很难绷。一眼DP。\(dp_{i,j}\)表示前\(i\)个宿舍,现在有连续\(j\)个灯亮大于\(B\),方案数。\(dp_{i,0}=\max(\min(B,r_i)-l_i+1,0)\cdot\sum\limits_{j=0}^{A-1}dp_{i-1,j}\)。\(dp_{i......
  • CF8E 题解
    blog。抽象意义上单杀了。首先第一位必定为\(0\),然后取反串就不用去考虑了。\(n\le50\),考虑爆搜。搜整个串的前一半(设半长为\(M=\left\lfloor\dfracn2\right\rfloor\),前一半的串在十进制下值为\(v\)),后半段的数量可以计算:整个串最后一位是\(0\),只需满足逆序串。\(n\)为......
  • P7701 [CCC2014] 提前交卷 题解
    目录DescriptionSolutionCodeDescription在一个教室里有\(n\)排座位,每排有\(6\)个,从左至右标号分别为ABCDEF,其中C和D中有过道,通往教室前端和后端的两个房间,每个房间最开始没有人,每个座位上开始都有人。有\(m\)个不同的学生会依次提前交卷,先从这一排走到过道上,在从......
  • 鼠标拖拽拖动盒子时,与盒子内某些点击事件冲突问题解决
    问题:拖动时会触发圆球的点击事件解决鼠标拖动盒子时,将moving设为true意为正在拖动盒子,此时将class="move"遮挡容器展示在悬浮球上层,以覆盖悬浮球,此时也就不存在触发悬浮球点击事件的冲突了;鼠标拖动完盒子弹起时再将 moving设为false意为不在拖动盒子(遮挡容器class=......
  • C++调用Python3实战,和PyImport_ImportModule返回NULL问题解决
    LinuxC++调用Python3入门准备以下面的目录结构演示如何在LinuxC/C++调用python3。|--hello.py|--main.cpp|--CMakeLists.txt hello.py:python的脚本,里面有2个函数main.cpp:c++函数CMakeLists.txt:Cmake文件,生成makefilepython脚本示例python脚本hello.py内容如下,......
  • 赛前集训11天题解大总
    Day1kitty核心思路:将转移过程中的方案加入转移矩阵,边转移边累加stringdp设计:\(f[i][x][y]\)表示长度为\(i\),第一段以\(x\)结尾,且\(x\leqslantp\),第二段以\(p\)开头,以\(y\)结尾的两段完全相同的序列的对数。对于每个\(p\)答案就是\(\sum_{i=1}^{n}i\cdotf[i][x......
  • 题解:Feel Good
    题目链接依然枚举每个位置作为最小值的情况,记录“值/下标”二元组,按第一维从大到小排序后,每次将第二位的位置在序列中标成\(1\),那么选择的一定是序列里一个\(1\)的极长段。加入一个位置检查其左右是否加入过,如果加入过就用并查集合并掉,同时维护极长段的和/左右端点是简单的,复......
  • 【洛谷 P2141】[NOIP2014 普及组] 珠心算测验 题解(集合+多重循环)
    [NOIP2014普及组]珠心算测验题目描述珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练,既能够开发智力,又能够为日常生活带来很多便利,因而在很多学校得到普及。某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他随机生成一个正整数集合......
  • B3871 题解
    题目链接题意简述给定一个正整数\(N\),将它的因数分解式按规定输出。题目分析模拟题意即可。具体地,我们可以枚举\(2\)到\(\lfloor\sqrtN\rfloor\)中所有数\(i\),如果\(i\)能整除\(N\),则不断地从\(N\)中除掉\(i\),直到\(i\)不再能整除\(N\),在这个过程中,我们同......