【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP

cyyz&qhyz&lwyz&gryz弱校胡策 命题人:cyyz ws_fqk

T3暴力写挫了 50+10+0滚粗辣!


奇妙的约会(appointment.cpp/c/pas)

【问题描述】

DQS和sxb在网上结识后成为了非常好的朋友,并且都有着惊人

的OI水平。在NOI2333的比赛中,两人均拿到了金牌,并保送进入

HU/PKU。于是两人决定在这喜大普奔的时刻进行面基。

NOI2333参赛选手众多,所以安排了n个考点,DQS在1号考点,

而sxb在n号考点。由于是举办全国性赛事的城市,自然有许多奇妙

的性质:

某些考点会组成一个集合, 集合中的考点两两之间的距离为一固

定值,且一个考点可以属于多个集合。

形式的说:有n个考点,m个集合,集合i中包含s_i个考点,

且这s_i个考点之间两两距离为t_i。

金牌爷的时间都非常珍贵,现在他们想知道,两人同时出发,他

们到达同一个考点见面所需的最短时间是多少?并且他们想知道, 满

足时间最短的考点分别是哪些?

【输入】

第一行包含两个整数n,m,意义如题意所说。

接下来m行,首先每行会有两个整数,分别代表t_i,s_i,接下

来包含s_i个整数,表示集合i中所含的考点编号。

【输出】

若两人无法见面,则输出“impossible”。

否则,第一行输出一个整数,表示最短时间。

第二行输出满足时间最短的考点编号,相邻的用空格隔开。

【样例输入】

5 4
1 3 1 2 3
2 2 3 4
10 2 1 5
3 3 3 4 5

【样例输出】

3
3 4

【数据范围】

对于50%的数据

N<=1000,sigma(s_i)<=1000

对于100%的数据

2<=n<=10^5,1<=t_i<=10^9,s_i>0,sigma(s_i)<=10^6



n^2建边 50分

%学娣 @Loi_a 的玄学暴力在开O2O3的前提下A掉此题…

可以发现每个集合并不需要建这么多边。新建两个点表示次集合的入点、出点,然后集合中每个点向入点建权值为0的边,出点向每个点建权值为0的边,入点向出点建权值为t的边,跑两遍最短路即可。

据说原题是doc出的题?

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
const int SZ = 3000010;
const int INF = 1000000010;

int head[SZ],nxt[SZ];

struct edge{
    int t;
    LL d;
}l[SZ];

void build(int f,int t,LL d)
{
    static int tot = 1;
    l[++ tot].t = t;
    l[tot].d = d;
    nxt[tot] = head[f];
    head[f] = tot;
}

LL dist1[SZ],dist2[SZ];

struct Heap{
    int u;
    LL d;
    Heap(int a = 0,LL b = 0) : u(a),d(b) {}
};

bool operator <(Heap a,Heap b)
{
    return a.d > b.d;
}

priority_queue<Heap> q;

bool vis[SZ];

void spfa(int s)
{
    memset(dist2,63,sizeof(dist2));
    memset(vis,0,sizeof(vis));
    dist2[s] = 0;
    q.push(Heap(s,0));
    while(q.size())
    {
        int u = q.top().u; q.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(dist2[v] > dist2[u] + l[i].d)
            {
                dist2[v] = dist2[u] + l[i].d;
                q.push(Heap(v,dist2[v]));
            }
        }
    }
}

int main()
{
    freopen("appointment.in","r",stdin);
    freopen("appointment.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i ++)
    {
        int t,s;
        scanf("%d%d",&t,&s);
        int in = i + 100000,out = i + 100000 * 2;
        build(in,out,t);
        while(s --)
        {
            int x; scanf("%d",&x);
            build(x,in,0); build(out,x,0);
        }
    }
    spfa(1);
    for(int i = 1;i <= n;i ++)
        dist1[i] = dist2[i];
    spfa(n);

    LL maxd = INF;
    for(int i = 1;i <= n;i ++)
        maxd = min(maxd,max(dist1[i],dist2[i]));
    if(maxd >= INF)
        { puts("impossible"); return 0; }
    printf("%lld\n",maxd);
    for(int i = 1;i <= n;i ++)
        if(max(dist1[i],dist2[i]) == maxd)
            printf("%d ",i);
    fclose(stdin); fclose(stdout);
    return 0;
}


粗心的特派员(careless.cpp/c/pas)

【问题描述】

在APIO&CTSC2016名单下发时,yzy和fqk惊奇的发现名单中没

有他们的名字,于是急忙联系了山东省特派员豆包哥lpy。却被很无

奈的告知报名表被当成垃圾邮件扔进了垃圾桶。

粗心的特派员总是会做一些让人啼笑皆非的事情, 比如在给各位

选手回复邮件的时候,经常会因为眼花而发错人。但虽然眼花,所以

误发的范围也不会太大。具体地说,本应该发给第i个人的邮件,可

能会不小心发给编号为[i-2,i+2]的人, 比如按照NOIP名单的顺序应

该是lct1999,Oxer,TA,fye,davidxu,而本应该发给TA的邮件却可

能发给这五位神犇中的任意一位。 但是他会保证每人都会且仅会收到

一封邮件。现在他想知道,他要将n封邮件发给n位选手,会有多少

种不同的结果?由于答案可能很大,请对10^9+7取模后输出。

【输入】

一行,一个整数n。

【输出】

一行,一个整数表示答案。

【样例输入】

4

【样例输出】

14

【数据范围】

对于10%的数据,n<=10

对于40%的数据,n<=500000

对于100%的数据,n<=10^16



第i个数可以放到[i-2,i+2]的区间内,求长度为n的数列的合法排列方案数。

出题人给的做法是状压DP,dp[i][S]表示当前填到第i个数,[i-2,i+2]填/不填的状态是S的方案数。要时刻保持[1,i-3]全为1,这样就能转移了。

发现每次转移都是一样的,可以矩阵优化。32*32的矩阵。

玄学做法:暴力跑出前十项,然后强行设系数然后高斯消元…最后发现是f[n]=2f[n-1]+2[n-3]-f[n-5]……事实证明这是对的,然后矩阵就行了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
const int SZ = 500010;
const int INF = 1000000010;
const int mod = 1000000007;

struct matrix{
    int n,m;
    int num[20][20];
    matrix(int a = 0,int b = 0) :n(a),m(b) { memset(num,0,sizeof(num)); }
};

matrix operator *(const matrix &a,const matrix &b)
{
    matrix ans(a.n,b.m);
    for(int i = 1;i <= ans.n;i ++)
        for(int j = 1;j <= ans.m;j ++)
            for(int k = 1;k <= a.m;k ++)
                ans.num[i][j] = (ans.num[i][j] + (LL)a.num[i][k] * b.num[k][j] % mod) % mod;
    return ans;
}

matrix ans,f;

matrix ksm(matrix a,LL b)
{
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

void init()
{
    ans.n = 1; ans.m = 5;
    f.n = 5; f.m = 5;
    ans.num[1][1] = 31; ans.num[1][2] = 14; ans.num[1][3] = 6;  ans.num[1][4] = 2;  ans.num[1][5] = 1;

    f.num[1][1] = 2;    f.num[3][1] = 2;    f.num[5][1] = -1;
    f.num[1][2] = 1;    f.num[2][3] = 1;    f.num[3][4] = 1;    f.num[4][5] = 1;
}

int main()
{
    freopen("careless.in","r",stdin);
    freopen("careless.out","w",stdout);
    LL n;
    scanf("%lld",&n);
    init();
    if(n <= 5) printf("%d\n",ans.num[1][5 - n + 1]);
    else
    {
        ksm(f,n - 5);
        printf("%d\n",(ans.num[1][1] + mod) % mod);
    }
    return 0;
}

/*

1 1
2 2
3 6
4 14
5 31

f[n] = 2f[i - 1] + 2 * f[i - 3] - f[i - 5]

*/

Description

浩浩荡荡的cg大军发现了一座矿产资源极其丰富的城市,他们打算在这座城市实施新的采矿战略。这个城市可以看成一棵有n个节点的有根树,我们把每个节点用1到n的整数编号。为了方便起见,对于任何一个非根节点v,它任何一个祖先的编号都严格小于v。树上的每个节点表示一个矿点,每条边表示一条街道。作为cg大军的一个小队长,你拥有m个部下。你有一张二维的动态信息表,用Ti,j表示第i行第j列的数据。当你被允许开采某个区域时,你可以将你的部下分配至各个矿点。在第i个矿点安排j个人可以获得Ti,j单位的矿产。允许开采的区域是这样描述的:给你一对矿点(u,v),保证v是u的祖先(这里定义祖先包括u本身);u为你控制的区域,可以在以u为根的子树上任意分配部下;u到v的简单路径(不包括u但包括v,若u=v则包括u)为探险路径,在该路径上你可以选择至多一个矿点安排部下。你这次开采的收益为安排有部下的矿点的收益之和。

Input

输入的第一行包含5个正整数n、m、A、B、Q。n为矿点的个数,m为部下的数量。A、B、Q是与动态信息表有关的数据。第二行包含n-1个正整数,第i个数为Fi+1,表示节点i+1的父亲。接下来需要你用下文的方法依次生成n组数据,每组数据共m个。其中第i组的m个数据为信息表中第i行的m个数据。紧接着一行包含一个正整数C,表示事件的数量。最后给出C行,每行描述一个事件。每个事件会先给出一个0或1的整数。如果该数为0,则后面有一个正整数p,表示动态信息表有更新,你需要生成一组m个数据,来替换信息表中第p行的m个数据。如果该数为1,则后面有两个正整数u、v,表示出现了一个你可以开采的区域,你需要回答这次开采的收益。同一行的各个数之间均用一个空格隔开,没有多余的空格和换行。数据的生成方法如下:每次生成一组m个从小到大排列的数据,替换动态信息表的一行。其中,从小到大第j个数替换信息表中第j列的数。调用以下代码m次并排序得到一组数据。(注意可能会出现重复的数)函数GetInt A←((A xor B)+(B div X)+(B * X))and Y B←((A xor B)+(A div X)+(A * X))and Y 返回(A xor B)mod Q 其中A、B、Q均用32位有符号整数保存(C/C++的signed long int类型,pascal的longint类型),X=216(2的16次方),Y=231-1(2的31次方-1),xor为位异或运算,div为整除运算,and为位且运算,mod为取余运算。由于只保留了低31位,易得我们不用考虑数据的溢出问题。(注意每次A和B都会被改变)

Output

对于每个开采事件(开头为1的事件),输出一行一个整数,为每次的收益。

Sample Input

10 5 1 2 10

1 1 3 3 4 4 6 6 9

4

1 6 3

1 9 1

0 1

1 1 1

Sample Output

11

9

12

【样例说明】

最初的信息表如下

1   2   3   4   5

1   0   1   1   2   2

2   0   5   7   7   9

3   1   2   3   4   5

4   0   1   2   4   5

5   2   4   7   8   8

6   0   2   3   8   9

7   1   3   5   6   8

8   3   3   3   7   8

9   0   1   2   3   9

10  0   0   1   4   4

变化后的第1行为

1   1   1   1   4   7

第一次开采可以在矿点6、8、9、10任意安排,可以在矿点3或4中选取一个安排开采。一种最优安排是在矿点6安排4人,在矿点8安排1人。第二次开采可以在矿点9安排,可以在矿点6、4、3、1中选择一个安排。一种最优安排是在矿点9安排1人,在矿点6安排4人。

HINT

有50%的数据,对于满足2≤i≤n的整数i,Fi=i-1。这些数据中有40%的数据(即所有数据的20%)满足n≤500,m≤20,C≤500。除上述数据,另有40%的数据满足n≤500,m≤20,C≤500。对于100%的数据1≤n≤20000,1≤m≤50,1≤C≤2000。对于满足2≤i≤n的整数i,1≤Fi<i。1≤A,B≤231-1,1≤Q≤10000。

Source



题面太长,复制的bzoj的。

清橙:http://www.tsinsen.com/A1219

线段树每个点维护当前区间选一个点放i个人的最大价值,再来个随便放i个点的最大价值,然后合并的时候像背包那样合并就行了

毒瘤题。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
const int SZ = 100010;
const int INF = 1000000010;

int n,m,A,B,Q;

int get_int()
{
    const int X = 1 << 16;
    const int Y = (1ll << 31ll) - 1ll;
    A=((A^B)+(B/X)+(B*X))&Y;
    B=((A^B)+(A/X)+(A*X))&Y;
    return (A^B)%Q;
}

void scan(int &n)
{
    n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > ‘9‘ || a < ‘0‘) { if(a == ‘-‘) flag = 1; a = getchar(); }
    while(a <= ‘9‘ && a >= ‘0‘) { n = n * 10 + a - ‘0‘; a = getchar(); }
    if(flag) n = -n;
}

int head[SZ],nxt[SZ],to[SZ];

void build(int f,int t)
{
    static int tot = 1;
    to[++ tot] = t;
    nxt[tot] = head[f];
    head[f] = tot;
}

int top[SZ],sz[SZ],son[SZ],fa[SZ],deep[SZ];

void dfs_1(int u,int f)
{
    fa[u] = f;
    deep[u] = deep[f] + 1;
    sz[u] = 1;
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        dfs_1(v,u);
        sz[u] += sz[v];
        if(!son[u] || sz[son[u]] < sz[v]) son[u] = v;
    }
}

int pre[SZ],suf[SZ],dfs_clock = 0,intre[SZ];

void dfs_2(int u,int topu)
{
    top[u] = topu;
    pre[u] = ++ dfs_clock;
    intre[dfs_clock] = u;
    if(son[u]) dfs_2(son[u],topu);
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        if(v == son[u]) continue;
        dfs_2(v,v);
    }
    suf[u] = ++ dfs_clock;
}

int val[SZ][60];

struct segment{
    int l,r;
    int mx1[60],mx2[60]; //子树 链
}tree[SZ << 2];

void update(int p)
{
    memset(tree[p].mx1,0,sizeof(tree[p].mx1));
    int lch = p << 1,rch = p << 1 | 1;
//  for(int i = 0;i <= m;i ++)
//      for(int j = 0;j <= m - i;j ++)
//          tree[p].mx1[i + j] = max(tree[p].mx1[i + j],tree[lch].mx1[i] + tree[rch].mx1[j]);
    for(int i = m;i >= 0;i --)
        for(int j = i;j >= 0;j --)
            tree[p].mx1[i] = max(tree[p].mx1[i],tree[lch].mx1[j] + tree[rch].mx1[i - j]);

    for(int i = 1;i <= m;i ++) tree[p].mx2[i] = max(tree[lch].mx2[i],tree[rch].mx2[i]);
}

void build(int p,int l,int r)
{
    tree[p].l = l; tree[p].r = r;
    if(l == r)
    {
        for(int i = 1;i <= m;i ++)
            tree[p].mx1[i] = tree[p].mx2[i] = val[intre[l]][i];
        return ;
    }
    int mid = (l + r) >> 1;
    build(p << 1,l,mid); build(p << 1 | 1,mid + 1,r);
    update(p);
}

void change(int p,int pos)
{
    if(tree[p].l == tree[p].r)
    {
        for(int i = 1;i <= m;i ++)
            tree[p].mx1[i] = tree[p].mx2[i] = val[intre[pos]][i];
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(pos <= mid) change(p << 1,pos);
    else change(p << 1 | 1,pos);
    update(p);
}

int ans1[60],ans2[60];

void ask_ans1(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)
    {
        for(int i = m;i >= 0;i --)
            for(int j = i;j >= 0;j --)
                ans1[i] = max(ans1[i],ans1[i - j] + tree[p].mx1[j]);
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid) ask_ans1(p << 1,l,r);
    if(mid < r) ask_ans1(p << 1 | 1,l,r);
}

void ask_ans2(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)
    {
        for(int i = 1;i <= m;i ++)
            ans2[i] = max(ans2[i],tree[p].mx2[i]);
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid) ask_ans2(p << 1,l,r);
    if(mid < r) ask_ans2(p << 1 | 1,l,r);
}

void find_ans2(int x,int y)
{
    int fx = top[x],fy = top[y];
    while(fx != fy)
    {
        if(deep[fx] < deep[fy]) swap(fx,fy),swap(x,y);
        ask_ans2(1,pre[fx],pre[x]);
        x = fa[fx]; fx = top[x];
    }
    if(deep[x] > deep[y]) swap(x,y);
    ask_ans2(1,pre[x],pre[y]);
}

int main()
{
    freopen("energy.in","r",stdin);
    freopen("energy.out","w",stdout);
    scan(n); scan(m); scan(A); scan(B); scan(Q);
    for(int i = 2;i <= n;i ++)
    {
        int x;
        scan(x);
        build(x,i);
    }
    for(int i = 1;i <= n;i ++)
    {
        for(int j = 1;j <= m;j ++)
            val[i][j] = get_int();
        sort(val[i] + 1,val[i] + 1 + m);
    }
    dfs_1(1,0); dfs_2(1,1);
    build(1,1,dfs_clock);
    int C;
    scan(C);
    while(C --)
    {
        int opt;
        scan(opt);
        if(opt == 0)
        {
            int p;
            scan(p);
            for(int j = 1;j <= m;j ++)
                val[p][j] = get_int();
            sort(val[p] + 1,val[p] + 1 + m);
            change(1,pre[p]);
        }
        else
        {
            memset(ans1,0,sizeof(ans1));
            memset(ans2,0,sizeof(ans2));
            int u,v;
            scan(u); scan(v);
            ask_ans1(1,pre[u],suf[u]);
            if(u != v) find_ans2(v,fa[u]);
            int ans = 0;
            for(int i = 0;i <= m;i ++)
                ans = max(ans,ans1[i] + ans2[m - i]);
            printf("%d\n",ans);
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
时间: 2024-10-10 17:23:43

【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP的相关文章

弱省胡策系列简要题解

现在不是非常爽,感觉智商掉没了,就整理一下最近弱省胡策的题目吧. 其实题目质量还是很高的. 如果实在看不懂官方题解,说不定这里bb的能给您一些帮助呢? [弱省胡策]Round #0 A 20%数据,O(n4)傻逼dp. 40%数据,O(n3)傻逼dp. 100%数据,令f(x1,y1,x2,y2)表示从(x1,y1)走到(x2,y2)的路径条数.于是所有路径就是f(1,2,n?1,m)×f(2,1,n,m?1).然而两条路径可能在中间的某个点相交,我们找出最早的交点,并在这个交点互换两条路径的后

2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)

题目链接 题意 :小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100,告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的那个去做,问她最终有一个账号达到1000分需要做的比赛的次数的期望值. 思路 :可以直接用公式推出来用DP做,也可以列出210个方程组用高斯消元去做. (1)DP1:离散化.因为50,100,1000都是50的倍数,所以就看作1,2,20.这样做起来比较方便. 定义dp[i]为从 i 分数到达i+1

多校第九场:贪心+矩阵快速幂中间优化+线性递推&amp;线段树递推

HDU 4968 Improving the GPA 思路:贪心的搞吧!比赛的时候想了好久,然后才发现了点规律,然后乱搞1A. 因为贪心嘛!大的情况就是刚开始每个人的分数都是最大的最小值,即绩点4.0的最低分数85,然后最后一个数设为剩余的分数,然后如果小于60就从第一个分数补到这个分数来,然后最后一个分数还小于60,那就用第二个补--依次往下搞,那时我也不知道这样就搞出答案了,我还没证明这个对不对呢,哈哈. 小的情况:小的情况就是先假设每个人都是绩点最小的最大分数,即绩点2.0的最大分数69,

数据结构(并查集||树链剖分):HEOI 2016 tree

[注意事项] 为了体现增强版,题目限制和数据范围有所增强: 时间限制:1.5s 内存限制:128MB 对于15% 的数据,1<=N,Q<=1000. 对于35% 的数据,1<=N,Q<=10000. 对于50% 的数据,1<=N,Q<=100000,且数据均为官方数据. 对于100% 的数据,1<=N,Q<=1000000. 请注意常数因子对于程序运行的影响. 并查集很简单,并查集就是倒序处理,表示删除一个点的标记,删除后不会再加回来,删完后,合并当前点与其

2016 CCPC 网络赛 B 高斯消元 C 树形dp(待补) G 状压dp+容斥(待补) H 计算几何

2016 CCPC 网络赛 A - A water problem 水题,但读题有个坑,输入数字长度很大.. B - Zhu and 772002 题意:给出n个数(给出的每个数的质因子最大不超过2000),选出多个数相乘得b.问有多少种选法让b 为完全平方数. tags:高斯消元,求异或方程组解的个数.   好题 每个数先素数分解开.  对于2000以内的每个素数p[i],这n个数有奇数个p[i]则系数为1,偶数个则系数为0,最后n个数的p[i]系数异或和都要为0才会使得最后的积为完全平方数.

2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)

2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点的树,树上每个节点有自己的颜色 问你删除第i条边后形成的两颗子树有多少个相同的颜色 题解: 树链剖分写法: 对于每一种颜色来说,如果这个颜色是在单独的一颗子树中,那么就不会对其他的边产生贡献,所以我们单独对每一种颜色对边的贡献讨论,如果这个颜色只有一个,那么就不会产生贡献,否则,他就可以在两个相同颜

HDU 5833 Zhu and 772002(高斯消元)——2016中国大学生程序设计竞赛 - 网络选拔赛

传送门 Zhu and 772002 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 48    Accepted Submission(s): 16 Problem Description Zhu and 772002 are both good at math. One day, Zhu wants to test the abili

【弱省胡策】Round #6 String 解题报告

感觉这个题好神啊. 首先我们只管 $a = b$ 的情况,那么我们自然就可以把这个串对 $a$ 取模,然后用 KMP 求出能弄出几个其他的 B 串. 具体就是把串先倍长,然后倒过来,然后求 $Next$ 数组,然后从 $2n$ 开始沿着 $Next[]$ 跳,直到跳到 $\le n$ 的时候停止,看哪些位置被跳到了,哪些位置就是合法的. 问题是现在 $a \neq b$ 怎么办..? 我猜啊,我们可以求出限制是 $a$ 的倍数时,哪些 B 串是合法的,再求出限制是 $b$ 的倍数是,哪些是合法的

【弱省胡策】Round #7 Rectangle 解题报告

orz PoPoQQQ 的神题. 我的想法是:给每一个高度都维护一个 $01$ 序列,大概就是维护一个 $Map[i][j]$ 的矩阵,然后 $Map[i][j]$ 表示第 $i$ 根柱子的高度是否 $\ge j$. 那么怎么维护 $Map[i][j]$ 呢..? 首先我们把柱子按照高度从小到大排序,然后依次给每个高度建主席树,初始时 $Map[i][0]$ 全是 $1$,然后如果当前高度 $i$ 比某个柱子 $j$ 的高度要大了,那么就单点修改 $Map[i][j]$,然后这个就是主席树动态开