Logo lxn 的博客

博客

Contest Solution - 2026/09/28 13:33

avatar lxn 2026-09-28 13:33:40

题解

ZOM (zom)

考察点:倍增求 lca。

首先注意到,我们的求LCA可以用倍增维护。

总所周知,我们删除一个叶子节点的倍增信息是容易的,而倍增又是可以动态加入的(考虑倍增向上跳的过程)。

因此,我们叶子节点的倍增信息是可以直接更新的。

然后就做完了,复杂度 $O(n \log n)$。

为什么倍增可以动态加入呢,因为每次加入他的时候祖先的倍增信息都是完好的,因此可以直接维护。

骰子 (dice)

$n \leq 8$

暴力枚举每个骰子选或不选即可。

$n \leq 50$

我们考虑一个 dp。

我们设 $f_{i,j,k}$ 表示前 $i$ 个骰子选了 $j$ 个且和为 $k$ 的方案数。

转移只要枚举这一个骰子是啥就好了。

复杂度 $O(n^4)$。

$n \leq 200$

不难注意到转移的是一段区间,我们使用前缀和优化 dp 即可做到 $O(n^3)$ ,利用滚动数组优化空间即可通过。

忍俊不禁 (laugh)

考察点:乘法逆元,组合数,高斯消元,简单推式子。

sub1

这个部分保证 $m=0$。

也就是说 $a_n=qa_{n-1}+p$。

对于这种题我们有一个套路,把两边都加上一个数,使得柿子化为这个形式:$a_n+t=q(a_{n-1}+t)$。

从而得出 $a_{n}+t$ 是一个等比数列,就很方便的得出通项为 $(a_1+t)q^{n-1}-t$,题目中则需要输出带 $q^n$ 的,把系数除 $q$ 即可。

不难注意到 $t$ 满足 $p+t=qt$,直接解方程即可。

sub2

方法和 sub1 类似,设两边加上的是 $kx+b$ 即可。

sub3

$x_i$ 直接这里写作 $a_i$。

首先我们设 $F\left(n\right)=\sum_{i=0}^{m}a_in^i$。

假设两边加上的多项式是 $G\left(n\right)=\sum_{i=0}^{m}b_in^i$。

那么我们有 $F(n)+G(n)=qG(n-1)$。

我们对于 $G(n-1)$ 利用二项式定理进行展开: $$ G(n-1)=\sum_{j=0}^{m}n^j\sum_{i=j}^{m}(-1)^{i-j}\binom{i}{j}b_i $$ 进行系数对比: $$ q(\sum_{i=j}^{m}(-1)^{i-j}\binom{i}{j}b_i)-b_j=a_j $$ 不难注意到这是一个关于 $b$ 的 $m+1$ 元方程组,利用模意义高斯消元进行求解即可。

答案即为 $\frac{a_1+G(1)}{q} \times q^n-G(n)$。

代码:

#include<bits/stdc++.h>
using namespace std;
namespace gza{
    #define int long long
    #define pb push_back
    #define MT int TTT=R;while(TTT--)
    #define pc putchar
    #define R read()
    #define fo(i,a,b) for(int i=a;i<=b;i++)
    #define rep(i,a,b) for(int i=a;i>=b;i--)
    #define m1(a,b) memset(a,b,sizeof a)
    namespace IO
    {
        inline int read()
        {
            int x=0;
            char ch=getchar();
            bool f=0;
            while(!isdigit(ch)){if(ch=='-') f=1;ch=getchar();}
            while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
            if(f) x=-x;
            return x;    
        }
        template<typename T> inline void write(T x)
        {
            if(x<0) pc('-'),x=-x;
            if(x>9) write(x/10);
            pc(x%10+'0');
        }
    };
    namespace math
    {
        inline int gcd(int a,int b)
        {
            int az=__builtin_ctz(a),bz=__builtin_ctz(b),z=(az>bz)?bz:az,t;
            b>>=bz;
            while(a) a>>=az,t=a-b,b=a,az=__builtin_ctz(t<0?-t:t),a=t<0?-t:t;
            return b<<z;
        }
        inline int qmi(int a,int b,int p)
        {
            int res=1;
            while(b)
            {
                if(b&1) res=res*a%p;
                a=a*a%p;
                b>>=1;
            }
            return res;
        }
        const int MAXN=2e6+10;
        int my_fac[MAXN],my_inv[MAXN];
        void init_binom(int mod)
        {
            my_fac[0]=1;fo(i,1,min(MAXN,mod)-1) my_fac[i]=my_fac[i-1]*i%mod;
            my_inv[min(MAXN,mod)-1]=qmi(my_fac[min(MAXN,mod)-1],mod-2,mod);rep(i,min(MAXN,mod)-2,0) my_inv[i]=my_inv[i+1]*(i+1)%mod;
        }
        int binom(int a,int b,int mod)
        {
            return my_fac[a]*my_inv[b]%mod*my_inv[a-b]%mod;
        }
    };
    using namespace IO;
    using namespace math;

    const int N=310,mod=998244353;
    int m,a1,q;
    int a[N],b[N];
    int d[N][N];
    void main(){
        freopen("laugh.in","r",stdin);
        freopen("laugh.out","w",stdout);
        init_binom(mod);
        m=R,a1=R,q=R;
        fo(i,0,m) a[i]=R;
        fo(j,0,m)
        {
            fo(i,j,m) d[j+1][i+1]=q*(((i-j)%2!=0)?mod-binom(i,j,mod):binom(i,j,mod))%mod;
            (d[j+1][j+1]+=mod-1)%=mod;
            d[j+1][m+2]=a[j];
        }
        int n=m+1;
        fo(i,1,n)
        {
            if(!d[i][i])
            {
                int maxi=0;
                for(int j=i+1;j<=n&&!maxi;j++) if(d[j][i]) maxi=j;
                if(!maxi) continue;
                fo(j,i,n+1) swap(d[i][j],d[maxi][j]);
            }
            fo(j,i+1,n)
            {
                int tmp=d[j][i];    
                if(tmp)
                {
                    fo(k,i,n+1)
                        d[j][k]=(d[j][k]*d[i][i]%mod-d[i][k]*tmp%mod+mod)%mod;
                }
            }
        }
        rep(i,n,1)
        {
            fo(j,i+1,n) d[i][n+1]=(d[i][n+1]-b[j]*d[i][j]%mod+mod)%mod;
            b[i]=d[i][n+1]*qmi(d[i][i],mod-2,mod)%mod;
        }
        int sum=0;
        fo(i,1,n) (sum+=b[i])%=mod;
        write((a1+sum)%mod*qmi(q,mod-2,mod)%mod),puts("");
        fo(i,1,n) write((mod-b[i])%mod),pc(' ');
    }
}
signed main(){

    gza::main();
}

追逐极光者 (aurora)

考察点:DP。

Sub1 & Sub2

暴力。

$2^m$ 枚举链的选择情况,$poly(n)$ 时间 check 即可。时间复杂度 $O(2^mpoly(n))$

Sub4

等价于选最大价值不交区间,我们将区间挂在右端点上。

令 $f_i$ 表示前 $i$ 个点最大价值,$f_i = \max_j f_{l_j - 1} + w_j$

选择喜欢的 DS 优化即可。

Sub3

这一档是留给做出了奇怪 $O(n^2m)$ 做法,或常数巨大的选手或者在一些细节上实现或维护不精导致复杂度错误的选手的。

Sub5

由链的情况启发,我们考虑将路径挂在 Lca 处。

令:$f_x$ 表示以 $x$ 为根的子树的答案,考虑 $Lca$ 为 $x$ 的路径,令 $S$ 表示红线下挂着的所有儿子组成的集合,有 $f_x = \sum_{y\in S}{f_y}$

由于是二叉树,对于跨过子树的链,我们暴力统计答案即可,对于单子树的链,我们记录一下 $L_f$ 与 $R_f$ 最后合并到 $f$ 上就好了。

Sub6

对于多叉树,麻烦的事情是一个 Lca 处会有多条链被选,而被选一条链就会改变一辆棵子树的贡献。

我们考虑在状态里多记一些东西:设 $f_{x,S}$ 表示以 $x$ 为根的子树且不考虑集合 $S$ 中儿子子树贡献的答案。

首先不考虑 Lca 处的链,用 $f_{Son,0}$ 处理出所有 $f_{x,S}$

然后拿链来做更新,我们实际上就是在做一个状态压缩的背包。

我们记 $Id_x$ 表示 $x$ 他父亲中 $x$ 的儿子编号,则有 $f_{x, S\or u\or v} = f_{u,0} + f_{v,0} + \sum_{y\in S} f_{fa_y,Id_y}$

然后暴力做背包·就行。


提高级

题目名称 ZOM 骰子 忍俊不禁 追逐极光者
题目类型 传统型 传统型 传统型 传统型
目录 zom dice laugh aurora
可执行文件名 zom dice laugh aurora
输入文件名 zom.in dice.in laugh.in aurora.in
输出文件名 zom.out dice.out laugh.out aurora.out
每个测试点时限 2000ms 1000ms 1000ms 1000ms
内存限制 1024mb 256mb 256mb 256mb
子任务数目 20 20 10 20
测试点是否等分 是 是 是 是

提交源程序文件名

对于C++语言 zom.cpp dice.cpp laugh.cpp aurora.cpp

编译选项

对于C++语言 -O2 -lm

注意事项(请仔细阅读)

  • 文件名(程序名和输入输出文件名)必须使用英文小写。
  • C/C++中函数main()的返回值类型必须是int,程序正常结束时的返回值必须是0。
  • 若无特殊说明,结果的比较方式为全文比较(过滤行末空格及文末回车)。
  • 程序可使用的栈空间内存限制与题目的内存限制一致。
  • 只提供Linux格式附加样例文件。

ZOM (zom)

题目背景

居住在 ZOM 国度的小 S 已经完成了种树的基本目标,现在他要对他的树进行一些修改。

题目描述

​ 给定一棵 $n$ 个点的树和 $q$ 次操作,$1$ 为树的根,分为两种:

  1. 给定 $u,v$,将 $u$ 的父亲修改为 $v$,保证 $u$ 是叶子结点。
  2. 给定 $u,v$,查询 $u$ 和 $v$ 的最近公共祖先。

输入格式

第一行输入两个正整数 $n,q$。

第 $2\sim n$ 行,每行输入两个正整数 $u,v$,表示一条树边。

第 $n+1\sim n+q$ 行,每行三个正整数 $op,u,v$。其中 $op$ 表示操作种类,$u,v$ 含义如题面所述。

输出格式

对于每一个查询操作,输出一行一个整数,表示你的答案。

样例 #1

样例输入 #1

3 3
1 2
1 3
2 2 3
1 3 2 
2 2 3

样例输出 #1

1
2

提示

对于 $30\%$ 的数据,保证 $1\leq n,q\leq 1000$

对于 $60\%$ 的数据,保证 $1\leq n,q\leq 2\times 10^5$

对于另外 $10\%$ 的数据,保证不存在操作 $1$。

对于 $100\%$ 的数据,保证 $1\leq n,q\leq 10^6$,所有输入均合法。

骰子 (dice)

题目描述

小 C 喜欢骰子,尽管这些骰子非常奇怪。

小 C 的手中有 $n$ 个不同的骰子,其中第 $i$ 个骰子是 $a_i$ 面骰子,每个面上的数字依次为 $1,2,3,\cdots,a_i$。

现在,小 C 要选择其中的 $k$ 个骰子,并对于这 $k$ 个骰子中的每一个骰子,选择一个面朝上。

小 C 想要知道,共有多少种不同的选择骰子和选择朝上的面的方案,可以使所有选择的骰子的朝上的面上的数字之和不大于 $p$。

两种选择骰子和选择朝上的面的方案不同,当且仅当存在一个只在其中一种选择方案中出现的骰子或存在一个同时出现在这两种选择方案的骰子的朝上的面上的数字不同。

由于答案可能很大,所以你只需要输出答案对 $998244353$ 取模的结果。

输入格式

第一行三个整数 $n,k,p$。

第二行 $n$ 个整数,表示数组 $a$。

输出格式

一行一个整数,表示满足条件的不同的选择骰子和选择朝上的面的方案数对 $998244353$ 取模的结果。

样例 #1

样例输入 #1

3 2 5
3 4 6

样例输出 #1

28

样例 #2

样例输入 #2

4 4 13
6 4 5 3

样例输出 #2

296

提示

【样例 #3】

见附加文件中的 dice/dice3.in 与 dice/dice3.ans。

该样例满足测试点 $8$ 的限制。

【样例 #4】

见附加文件中的 dice/dice4.in 与 dice/dice4.ans。

该样例满足测试点 $11$ 的限制。

【样例 #5】

见附加文件中的 dice/dice5.in 与 dice/dice5.ans。

该样例满足测试点 $13$ 的限制。

【样例 #6】

见附加文件中的 dice/dice6.in 与 dice/dice6.ans。

该样例满足测试点 $20$ 的限制。

【数据范围】

对于 $100\%$ 的数据,$1 \le k \le n \le 200$,$1 \le p \le 1000$,$1 \le a_i \le 10^9$。

测试点编号 $n \le$ $p \le$ $a_i \le$ 特殊性质
$1$ $8$ $8$ $8$ 是
$2\sim3$ $8$ $1000$ $8$ 是
$4\sim5$ $8$ $12$ $10^9$ 否
$6\sim7$ $50$ $80$ $80$ 否
$8\sim10$ $50$ $80$ $10^9$ 否
$11\sim12$ $200$ $1000$ $1$ 否
$13\sim15$ $200$ $400$ $10^9$ 是
$16\sim20$ $200$ $1000$ $10^9$ 否

特殊性质:保证 $k=n$。

忍俊不禁 (laugh)

题目背景

忍俊不禁题。会做之后,你会笑出声来。

题目描述

给定 $A_1$,求一个序列 $A$ 的通项。

序列 $A$ 满足 $A_n=qA_{n-1}+\sum_{i=0}^{m}n^ix_i$。

其中 $q$ 和序列 $x$ 都是给定的。

如果输出 $A$ 的通项见输出格式。

输入格式

第一行三个整数 $m,A_1,q$,含义见题面。

第二行 $m+1$ 个整数,用空格隔开,第 $i$ 个整数代表 $x_{i-1}$。

输出格式

我们保证 $A$ 的通项为 $A_n=t \times q^n+\sum_{i=0}^{m}y_in^i$ 的格式。且所有输出的数字都保证为有理数,要求输出的结果对于 $998244353$ 取模。

第一行输出一个正整数 $t$,含义见上段。

第二行输出 $m+1$ 个正整数,用空格隔开,第 $i$ 个数代表 $y_{i-1}$。

样例 #1

样例输入 #1

0 3 3
0

样例输出 #1

1
0

样例输入 #2

见下发文件中的 ex_laugh1.in。

样例输出 #2

见下发文件中的 ex_laugh1.ans。

提示

对于 $10\%$ 的数据,保证 $m=0$。

对于 $30\%$ 的数据,保证 $m \leq 1$。

对于 $100\%$ 的数据,保证 $1 \leq m \leq 300$,$0 \leq a_1,x_i(0 \leq i \leq m)<998244353$,$2 \leq q <998244353$。

数据有一定的梯度。

追逐极光者 (aurora)

题目背景

$\text「这人真有意思,明明就在极光城里,却在寻找极光城。」$

$\color{red} \text「不……他从未抵达极光城……」$

从此以后,极光城里多了个找极光城的疯子。

题目描述

极光城形如一颗 $n$ 个点的树,每个点最多只连接了 $10$ 条双向的街道。

$\color{red} \text「你在找什么?」$

$\text「我在找极光城。」$

$\color{red} \text「这里就是极光城。」$

$\text「我快找到了……我一定能找到的!」$

同时,有 $m$ 种风筝在极光城上空飘荡,在 $u_i$ 到 $v_i$ 间飘荡着 $w_i$ 只第 $i$ 种风筝。

Donald 想要顺着风筝找极光城,他可以在极光城每条街道停留并追寻风筝,且 Donald 在每条街道只能追寻其中一种风筝,如果他完整的经过了第 $i$ 种风筝飘荡的路线,则他可以获得这 $w_i$ 只风筝。

Donald 希望获得最多的风筝,他认为这样就能找到极光城了,请 $\color{red} \text「你」$ 告诉他最多能获得多少风筝。

$\text「极光城有风筝……我也有风筝……顺着风筝……我一定能去极光城!」$

$\text「我就快找到极光城了……」$

$\text「我就快找到了!」$

形式化题意

给定一颗 $n$ 个点的树,树上每个点最大度数为 $10$,以及树上 $m$ 条路径,路径有权值 $w$,你需要选出若干路径使得选出的路径边无交,问最大价值。

$n,m\leq 1000,w_i\leq 10^9$

输入格式

第一行两个整数 $n, m$,表示点数以及路径数。

接下来 $n-1$ 行,每行两个整数 $u,v$ 表示 $u,v$ 之间有条双向边。

接下来 $m$ 行,每行三个整数 $u,v,w$ 表示 $u,v$ 为路径的两个端点,$w$ 为这条路径的权值。

输出格式

输出一行一个整数,表示最大价值。

输入输出样例

样例#1

样例输入#1

7 3
1 2
1 3
2 4
2 5
3 6
5 7
1 6 1
1 4 3
4 7 1

样例输出#1

4

样例 #2

样例输入 #2

见下发文件中的 ex_aurora1.in。

样例输出 #2

见下发文件中的 ex_aurora1.ans。

样例 #3

样例输入 #3

见下发文件中的 ex_aurora2.in。

样例输出 #3

见下发文件中的 ex_aurora2.ans。

提示

对于所有数据满足 $n,m\leq 1000,w\leq 10^9$

数据点编号 $n$ $m$ 特殊性质
1~2 $10$ $10$
2~4 $10^3$ $10$
5~8 $100$ $100$
9~10 $10^3$ $10^3$ 保证树是一条链
11~12 $10^3$ $10^3$ 保证每个点度数最大为 $3$
13~20 $10^3$ $10^3$ 每个点的度数不超过 10

题解:B4428 [CSP-X2025 山东] 勇者斗恶龙

avatar lxn 2025-12-01 12:57:49

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-11-10 17:05:45

题目分析

对序列 $a_1,a_2,...a_n$ 来说,只有连续的相等序列段才需要修改。

对于第 $i$ 个元素来说,只可能因为与左侧相等被修改一次,与右侧相等被修改一次,最多修改次数为两次。

因此,第 $i$ 个元素是否被修改只与第 $i-1$ 个元素相关,可以用动态规划解决该问题。

设 $dp_{i,0/1/2}$ 表示当前点被修改的次数。

当某个 $k$ 满足 $a_i+j \neq a_{i-1}+k$ 时,转移方程为 $dp_{i,j}=\min(dp_{i,j},dp_{i-1,k}+b_i\times j)$。

边界条件

我们要求最小值,所以 $dp$ 数组要初始化为最大值。

初始条件为 $dp_{0,0}=dp_{0,1}=dp_{0,2}=0$ .

参考代码

#include<bits\/stdc++.h>
using namespace std;
const int N=2e5+9;
typedef long long ll;
ll a[N],b[N],dp[N][3],n;\/\/对当前数来说f[i][0\/1]表示变了自己增加了多少,最多加2。 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
	\/\/可以分类讨论,感觉不如dp好写,dp可以无脑暴力枚举 
	
	memset(dp,0x3f,sizeof(dp)) ;
	a[0]=-2e9;
	dp[0][0]=dp[0][1]=dp[0][2]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<3;j++)
			for(int k=0;k<3;k++)
				if(a[i]+j!=a[i-1]+k) dp[i][j]=min(dp[i][j],dp[i-1][k]+j*b[i]);	
	} 
	cout<<min(dp[n][0],min(dp[n][1],dp[n][2]));
}

山东CSP-S考前注意事项

avatar lxn 2025-12-01 12:57:49

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-10-24 09:32:57

试机注意事项

  1. 存储检查:先确认可以存储的盘符
  2. 文件保护:建立测试文件并重启,检查保护是否开放
  3. 编译测试:编写简单代码进行编译运行测试

考试前准备

试机结束至考试开始期间

  • 编译标准:C++14 标准下不能使用 gets(),读取带空格的字符串使用:

    getline(cin, s);  \/\/ 或
    fgets();
    
  • 编辑器设置:

    • 开启自动保存选项
    • 修改编译参数: -O2 -std=c++14 -static -g -Wall -Wextra -Wl,--stack=536870912 占空间根据题目要求空间设置
    • 注意编译警告,确保函数有返回值
  • 代码验证:编译测试(考场未设置g++环境变量,熟练选手选做。)

    g++ -O2 -std=c++14 -Wall -Wl,--stack=1000000000 a.cpp -o a.exe
    

考试流程

文件操作规范

#include <cstdio>
using namespace std;

int main() {
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    
    \/\/ 代码逻辑
    
    return 0;
}

重要提醒:

  • freopen 放在流同步前面
  • 用了流同步,一定不要用scanf(),
  • cout不要用endl 要用"\n"
  • 使用流同步时不要用 fclose
  • 字符输出要仔细核对,使用写字板查看 .in 文件

读题审题策略

  1. 仔细阅读:不要节省读题时间,理解题意是得分基础
  2. 关注限制:注意时间、空间限制条件
  3. 细节把握:
    • 注意上下界和特殊条件
    • 仔细阅读子任务数据范围
  4. 策略选择:
    • 通读所有题目,选择可做题先做
    • 分析特殊数据,争取子任务分数
    • 避免死磕难题

考试技巧

心态与策略

信息奥赛考的是心态,打好暴力提升下限,沉稳分析提升上限!

  • 分任务拿分:不要死磕一个题目
  • 子程序设计:根据数据范围编写子程序,在主程序中灵活调用
  • 复杂度计算:分析数据范围,计算时空复杂度

数据分治技巧

\/\/ 示例:根据数据范围选择算法
if (n <= 1000) {
    \/\/ 使用暴力解法
    brute_force();
} else {
    \/\/ 使用优化算法
    optimized_solution();
}

常见问题与解决方案

初始化问题

  • 算法开始时进行必要的初始化
  • 将初始化代码作为算法的一部分编写

边界情况处理

  1. 整数溢出:中间过程可能爆 int/long long
  2. 数据范围:关注上下界和极限情况

多测试数据注意事项

\/\/ 清空数据结构
void clear_data() {
    \/\/ 使用 for 循环清空,避免 memset 误用
    for (int i = 0; i <= n; i++) {
        data[i] = 0;
    }
}

清空要点:

  • 不确定时全部清空
  • 注意边界位置
  • 多测时必须读完所有输入

数组管理

const int MAXN = 100000 + 10;  \/\/ 多开一些空间

int arr[MAXN];  \/\/ 使用常量定义数组大小

空间计算:

  • 线段树开4倍空间
  • 大数组要计算总空间
  • 动态开空间考虑极限情况
  • STL容器注意额外空间开销

溢出防护

\/\/ 加法和乘法检查溢出
long long result = (long long)a * b;  \/\/ 防止乘法溢出
int sum = a + b;  \/\/ 可能溢出,考虑用 long long

\/\/ 取模操作
result = (a * b) % MOD;  \/\/ 中间过程取模

变量管理

  • 易混淆变量使用明确命名
  • 使用注释标记重要变量
  • 注意运算符的优先级,多加括号。位移运算与算术运算。算术运算更高
  • 修改代码时检查所有相关位置

调试与验证

对拍策略

  1. 数据覆盖:测试上下界和极限数据
  2. 暴力验证:确保暴力解法正确性
  3. 减少重合:避免正解和暴力犯相同错误

RE问题排查

  1. STL安全:

    if (!container.empty()) {
        value = container.front();  \/\/ 判空后访问
    }
    
  2. 迭代器安全:避免在 begin() 处 -- 或 end() 处 ++

  3. 数据结构完整性:考虑空节点情况

  4. 清理调试代码:提交前移除所有调试语句

实用工具

文件比较工具

FC命令使用说明

# 基本用法
fc file1.out file2.out

# 忽略大小写
fc \/c file1.out file2.out

# 以ASCII方式比较
fc \/a file1.out file2.out

# 以二进制方式比较
fc \/b file1.out file2.out

# 显示不同行的行号
fc \/n file1.out file2.out

时间检测工具

#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;

#define time_now double(clock())\/CLOCKS_PER_SEC

int main() {
    double time = double(clock()) \/ CLOCKS_PER_SEC;
    while (1) {
        if (time_now - time > 0.8) break;
    }
    cout << fixed << setprecision(5) << time_now - time;
    return 0;
}

空间计算工具

#include <iostream>
using namespace std;

bool STSTST;
int a[114514];
int b[30][40000];
bool EDEDED;

int main() {
    cout << "USE " << (&EDEDED - &STSTST) \/ 1024.0 \/ 1024.0 << "MB" << endl;
    return 0;
}

考试结束前检查

  1. 最后15分钟:检查文件版本是否正确
  2. 文件操作:确认已取消调试用的文件输入输出注释
  3. 编译验证:修改文件后必须重新编译测试

祝各位考生在CSP2025中取得优异成绩!

CSP-J\/S近五年题型难度分析与备赛总结

avatar lxn 2025-12-01 12:57:49

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-10-24 09:23:10

CSP-J 2020-2024 题型难度分析

真题难度分布表

年份 题目 难度 算法
2020 优秀的拆分 入门 枚举
2020 直播获奖 普及 模拟、排序
2020 表达式 普及/提高 模拟、栈
2020 方格取数 普及/提高 动态规划DP
2021 分糖果 普及 数学
2021 插入排序 普及/提高 枚举、排序
2021 网络连接 普及/提高 模拟、字符串、STL
2021 小熊的果篮 普及/提高 模拟、STL、链表
2022 乘方 入门 枚举
2022 解密 普及 数学、二分
2022 逻辑表达式 普及/提高 模拟、栈、表达式求值
2022 上升点列 普及/提高 动态规划DP
2023 小苹果 普及 数学、找规律
2023 公路 普及 贪心、前缀和
2023 一元二次方程 普及/提高 模拟
2023 旅游巴士 普及/提高 图论、二分+BFS
2024 扑克牌 入门 模拟、STL
2024 地图探险 普及 模拟
2024 小木棍 普及/提高 动态规划DP、贪心
2024 接龙 提高/省选 动态规划DP、图论

CSP-S 2020-2024 题型难度分析

真题难度分布表

年份 题目 难度 算法
2020 儒略日 普及/提高 模拟、数学、二分
2020 动物园 普及/提高 数学、贪心、进制、位运算
2020 函数调用 提高/省选- 动态规划DP、拓扑排序
2020 贪吃蛇 NOI/NOI+/CTSC 贪心、队列、堆
2021 廊桥分配 普及/提高 模拟、前缀和、队列
2021 括号序列 提高/省选- 动态规划DP、区间DP
2021 回文 普及/提高 字符串、贪心
2021 交通规划 省选/NOI- 网络流、平面图
2022 假期计划 提高/省选- 广播、折半搜索
2022 策略游戏 普及+/提高 贪心、线段树、ST表
2022 星战 省选/NOI- 图论、哈希
2022 数据传输 省选/NOI- 动态规划DP、矩阵乘法
2023 密码锁 普及- 模拟、枚举
2023 消消乐 提高/省选- 动态规划DP、哈希
2023 结构体 提高/省选- 模拟
2023 种树 提高/省选- 贪心、二分
2024 决斗 普及- 贪心
2024 超速检测 普及+/提高 图论、最短路、次短路
2024 染色 提高/省选- 动态规划DP
2024 擂台游戏 NOI/NOI+/CTSC 贪心、递推、树形DP、差分

二、CSP-J 分析与备赛策略

总结与分析

  • 难度分布:以入门和普及-难度为主,每年会有1-2道普及+/提高甚至更高难度的题目用于区分层次。2024年出现了提高/省选难度的题目,说明难度有小幅提升趋势。

  • 算法考查:以模拟、枚举、数学(找规律、简单数论)、简单DP、基础数据结构(栈、链表、STL的简单应用)为主,注重对编程基础和逻辑思维的考查。

  • 趋势特点:题目越来越贴近实际应用场景,对代码的可读性和规范性要求有所提高;同时部分题目开始融合多个基础算法,考查综合运用能力(如2024年接龙结合了DP和图论)。

难度走势

  • 整体趋势:2020—2022 重基础+模拟+数学;2023—2024 出现明显算法进阶化(DP、图论、贪心占比上升)。

  • 题型分布:

    • 模拟题:约占40%
    • 数学/找规律题:约占25%
    • 动态规划DP:2020, 2022, 2024(方格取数、小木棍、接龙等)
    • 模拟与枚举:每年都有(扑克牌、地图探险、直播获奖等)
    • 数学与规律:2021-2023(分糖果、解密、小苹果等)
    • STL与链表:2021, 2024(小熊的果篮、扑克牌)
    • 图论与BFS:2023, 2024(旅游巴士、接龙)

题型分布特点

  1. 第1题:多为入门难度,考查枚举、模拟、基础数学
  2. 第2题:普及到普及+,考查模拟、排序、简单贪心、数学
  3. 第3题:普及+/提高,常考动态规划、表达式求值、图论、数据结构
  4. 第4题:普及+/提高到提高/省选,考查较复杂的动态规划、图论、贪心等

常考知识点

  • 基础算法:枚举、模拟、排序
  • 数学:简单数论、找规律、基础组合
  • 数据结构:栈、队列、链表、STL应用
  • 动态规划:线性DP、状态机DP
  • 图论:BFS、最短路径
  • 其他:贪心、二分、前缀和

备赛策略

  1. 打好基础:熟练掌握C++语法和STL
  2. 刷题重点:
    • 第1、2题:刷普及及以下难度的模拟、枚举、数学题
    • 第3题:重点练习动态规划、栈与队列、简单图论
    • 第4题:练习提高难度的DP、贪心、图论题
  3. 模拟实战训练:多做历年真题和模拟赛,适应比赛节奏
  4. 时间分配:前两题尽量快速AC,留足时间攻克后两题

备赛建议

阶段一(基础夯实)

  • 掌握:循环、数组、字符串、结构体
  • 刷题:洛谷普及-组、CSP-J历年T1-T2

阶段二(算法训练)

  • 专项:枚举+模拟+排序
  • 掌握:STL基础(vector, queue, map)

阶段三(进阶算法)

  • DP专题:路径型、序列型
  • 图论基础:BFS、DFS、最短路径(理解层面)

阶段四(冲刺实战)

  • 历年真题全模拟(限时4小时)
  • 调整策略:保证T1-T3全AC,T4拿部分分

三、CSP-S 分析与备赛策略

总结与分析

  • 难度分布:题目难度跨度大,从普及到NOI/CTSC级别均有涉及,每年都会有1-2道高难度(省选/NOI及以上)题目,同时也包含一定比例的普及+到提高级题目用于保底得分。

  • 算法考查:动态规划(DP)是绝对的核心考点,几乎每年都有多道题涉及;贪心、模拟、数学(数论、进制等)、图论(网络流、树链剖分等)、数据结构(线段树、ST表、堆、队列等)也是高频考点。

  • 趋势特点:高难度题目越来越注重算法的综合运用(如2024年擂台游戏融合了贪心、递推、树形DP、差分等多种算法),对选手的思维深度和代码实现能力要求较高。

难度走势

  • 总体趋势:
    • 2020~2021:稳中求进(动态规划、贪心)
    • 2022起:算法难度显著提升(图论、网络流、矩阵快速幂)
    • 2023~2024:CSP-S 已与 NOI 难度接轨

重点算法分布

模块 出现频率 常考题型
动态规划DP ★★★★☆ 函数调用、数据传输、染色
贪心算法 ★★★☆☆ 动物园、策略游戏、决斗
图论算法 ★★★☆☆ 交通规划、星战、擂台游戏
字符串与哈希 ★★★☆☆ 消消乐、回文、表达式
数学/位运算 ★★★☆☆ 动物园、密码锁
搜索与组合 ★★★☆☆ 假期计划、策略游戏

难度层级

等级 占比 特征
普及/提高 35% 模拟、贪心、基础DP
提高+/省选 45% 状压DP、区间DP、搜索剪枝
NOI级 20% 树形DP、网络流、复杂图论

题型分布特点

  1. 第1题:普及-到普及+/提高,考查模拟、数学、枚举
  2. 第2题:普及+/提高到提高/省选,考查贪心、数据结构、DP
  3. 第3题:提高/省选,考查较复杂的DP、图论、数据结构
  4. 第4题:省选/NOI-到NOI/CTSC,考查高级算法与复杂问题建模

常考知识点

  • 数据结构:线段树、ST表、哈希、队列、堆
  • 动态规划:区间DP、树形DP、状态压缩DP、动态DP
  • 图论:最短路、网络流、拓扑排序、LCA
  • 数学:数论、组合数学、矩阵快速幂

备赛策略

  1. 系统学习:

    • 掌握所有普及组知识点,并深入学习提高组内容
    • 重点学习动态规划、图论、数据结构
  2. 刷题方向:

    • 第1题:保证稳定AC,练习模拟、数学、枚举
    • 第2题:练习贪心、数据结构、基础DP
    • 第3题:重点练习提高/省选-难度的DP、图论、数据结构
    • 第4题:尝试理解题解,学习高级算法
  3. 模拟赛与复盘:

    • 定期参加模拟赛,严格计时
    • 每场赛后详细复盘,查漏补缺
  4. 时间管理:

    • 前两题控制在1.5小时内完成
    • 留足时间思考第3题,第4题尽量拿部分分

备赛建议

基础巩固(算法巩固)

  • 系统掌握:DFS/BFS、二分、贪心、堆、并查集
  • 刷题范围:CSP-S T1、T2,普及+到提高组题库

进阶训练(核心算法)

  • 深入学习:动态规划(线性DP、区间DP、状态压缩DP)
  • 图论专题:最短路、拓扑排序、网络流、LCA
  • 数据结构:线段树、树状数组、ST表

冲刺阶段(综合实战)

  • 模拟全卷训练(限时4小时)
  • 分析历年CSP-S T3-T4思路
  • 以「优化DP + 贪心」为主要突破点

进阶拓展(面向NOI)

  • 学习:矩阵快速幂、树形DP、差分约束
  • 推荐OJ:洛谷提高组专题、AtCoder DP Contest、Codeforces比赛、CSP历年题库

KOI2024R2

avatar lxn 2025-12-01 12:57:49

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-10-15 16:03:30

deepseek翻译官方题解

T2 P12648 [KOI 2024 Round 2] 路灯

“初等部第2题 / 中等部第1题. 路灯” 问题解答

作者:尹教俊

部分问题 1

从 x = 0 到 x = A₁ 的整数位置上的黑暗程度依次是 0, 1, ..., A₁。 此外,从 x = A₁ + 1 到 x = L 的整数位置上的黑暗程度依次是 1, 2, ..., L - A₁。

也就是说,由于排序后的黑暗程度值存在简单的规律,因此可以在比较 A₁ 和 L - A₁ 的大小后输出答案。

部分问题 2

根据题目中给出的公式,可以在 O(N) 时间内计算出一个位置的黑暗程度。

因此,计算所有位置的黑暗程度后进行排序,即可在 O(NL) 的时间复杂度内解决。

部分问题 3

路灯从 0 到 L 等间距分布。

因此,持续输出数字直到输出 K 个数为止:先输出 N 次 0,然后对于 1 及以上的整数,依次各输出 2(N - 1) 次即可。

部分问题 4

观察到以下情况:

  • 位置 0 ≤ x ≤ A₁ 的黑暗程度是 A₁ - x。
  • 位置 Aᵢ ≤ x ≤ Aᵢ₊₁ 的黑暗程度是 min{x - Aᵢ, Aᵢ₊₁ - x}。
  • 位置 A_N ≤ x ≤ L 的黑暗程度是 x - A_N。

对于每个区间,每个位置的黑暗程度可以在 O(1) 时间内计算出来。因此,可以在 O(L) 的时间复杂度内完成所有位置的黑暗程度计算及排序。

部分问题 5

以 N 个路灯的放置位置作为起点进行 BFS。

每当从队列中移除一个(位置,距离)对时,输出的距离值就是题目所要求的答案。

队列的插入和删除操作总共执行 O(N + K) 次。

在将点插入队列之前需要检查其是否已被访问过,这可以使用 std::set 等平衡二叉搜索树库来高效处理。

因此,总时间复杂度为 O((N + K)lg(N + K))。

也可以使用 O(N + K) 的线性时间和空间复杂度来解决此问题。

T3 P12649 [KOI 2024 Round 2] 收集彩球

“初等部第3题 / 中等部第2题. 收集颜色” 问题解答

作者:朴灿率

部分问题 1

由于 N ≤ 2,因此可以预先计算所有可能输入的答案并输出。

部分问题 2

由于 N ≤ 20,因此可以定义 dp[bit] 为状态为 bit 时从初始状态所需的最小移动次数,从而使用位运算的动态规划来解决问题。

将 bit 的第 i 位定义为:如果颜色为 i 的球在同一个箱子中则为 1,否则为 0。由于从每个状态能得出的当前球的箱子分配是唯一的,因此可以使用动态规划。

部分问题 5

设第 i 个箱子中下方球的颜色为 A_i,上方球的颜色为 B_i。考虑一个有 N 个顶点的有向图 G 和一个无向图 G'。对于所有的 A_i, B_i,在 G 中添加有向边 (A_i, B_i),在 G' 中添加无向边 (A_i, B_i)。如果 A_i = B_i,则在两个图中各添加一条指向自身的边。

在图 G' 中,所有顶点的度数恰好为 2,因此该图由简单环的集合构成。属于不同简单环的顶点互不影响,因此只需逐个处理图中的简单环即可。

如果这样构建的图 G' 中的每个环,在 G 中出度大于等于 2 的顶点不超过 1 个,则可以通过以下过程移动球,使得所有箱子中都装入相同颜色的球:

  • 任意选择一个出度小于等于 1 的顶点。
  • 将该顶点移动到空箱子中。
  • 现在重复以下过程,直到处理完一个环中的所有顶点: – 设刚才选择并移动的顶点为 u。 – 对于所有顶点 v,如果存在边 (v, u) 则将其移除。 – 任意选择一个在 G 中出度为 0 且在 G' 中度数小于等于 1 的顶点 w。 – 顶点 w 的出度为 0 意味着颜色为 w 的球都位于箱子的最顶部。 – 此外,度数为 1 意味着通过一次移动即可将与该颜色相同的球收集到一个箱子中。因此,选择颜色为 w 的两个球中的一个移动到另一个箱子,即可将球收集到一个箱子中。

然而,如果一个环中存在两个或更多出度为 2 的顶点,则无法将上述过程进行到底。否则,将一个环中包含的球移动至每个箱子都装有相同颜色球所需的移动次数如下:

  • 设环中包含的顶点数为 V。
  • 若 V = 1,则为 0 次。
  • 若 V > 1,则为 V + 1 次。

对所有环的上述次数求和,即可求得正确答案。

T4 P12652 [KOI 2024 Round 2] 拔树游戏

“中等部第4题 / 高等部第3题. 树提取” 问题解答

作者:李恩祖

部分问题 1

直接实现题目中给出的操作,可以在 O(N²) 的时间复杂度内解决问题。

部分问题 2

可以证明,在使用管理最小值、最大值的数据结构(如堆)的原理进行提取操作后,对于所有 2 ≤ i ≤ N,A_{P_i} < A_i 仍然成立。因此,根顶点始终是所有顶点中权重最小的。所以答案总是 1, 2, ..., N。

部分问题 3

假设某个顶点 u 有子节点 v₁, ..., v_k,不失一般性地,设 A_{v₁} < ... < A_{v_k}。如果 u 被选为特殊路径,那么具有最小权重的子节点 v₁ 也将被选为特殊路径。根据部分问题的条件,在提取操作之后,顶点 v₁ 的权重会变得更小,因此可知 v₁ 仍然会被选为特殊路径。也就是说,在以 v₁ 为根的子树中的所有顶点都被移除之前,v₁ 会一直被选为特殊路径。即,按照 v₁, ..., v_k 的顺序,这些顶点子树中的所有顶点将依次被选择。因此,从根顶点开始执行 DFS,并按照权重从小到大的顺序访问顶点,即可知 DFS 中的顶点访问顺序就是答案。

部分问题 4

如果只考虑度数为 3 及以上或者是叶子的顶点,可以创建一个压缩形式的树。这样创建的压缩树的边上将挂有一串顶点。对于压缩树中的每个顶点,使用优先队列来管理其相邻顶点的权重,而对于挂在压缩树边上的成串顶点,则使用队列进行管理。根据问题条件,压缩树的高度较小,因此可以在 O(20 log N) 的时间复杂度内找到特殊路径。沿着特殊路径向上移动,同时更新边的队列和顶点的优先队列,则提取操作也可以在 O(20 log N) 的时间复杂度内实现。总时间复杂度为 O(20N log N)。

部分问题 5

通过在子树中解决部分问题,并将其合并来解决父节点的问题。假设某个顶点 u 有子节点 v₁, ..., v_k,将以 v_i 为根的子树对应的答案序列记为 B_{v_i}[...]。这意味着当 v_i 包含在特殊路径中并执行提取操作时,顶点 v_i 的权重按照序列 B_{v_i}[...] 的顺序变化。我们必须利用此信息来找到以 u 为根的子树的答案序列 B_u[...]。显然,B_u[...] 的第一个数字是 A_u。现在,剩余的数字可以通过像执行归并排序一样,从 B_{v₁}[...], ..., B_{v_k}[...] 中选取最小的数字并添加到 B_u[...] 的末尾,重复此过程即可全部求出。

如果简单地实现此过程会导致时间超限,因此需要优化。在从序列 B_i[...] 的前面开始读取时,按照最大值更新的时刻将其划分为新的组的方式来管理组,并将组的代表值定义为该组最左边的数字。例如,序列 2, 1, 3, 5, 4, 8, 6, 7 被管理为 [2, 1], [3], [5, 4], [8, 6, 7]。这样划分组后,在将多个序列合并为一个时,同组的数字将始终保持在同一组中,并且是按照组的代表值顺序进行合并的。此外,代表值比第一个数字小的组将会合并成一个新的组。因此,使用优先队列来管理这些组,并运用"小集合合并到大集合"的技巧,可以在 O(N log² N) 的时间复杂度内解决问题。

再深入思考一下,可以归纳地证明,此解法最终所求的是在保持父-子顺序关系的排列中字典序最小的那个排列。因此,使用优先队列从根顶点开始选择权重最小的顶点,可以在 O(N log N) 的时间复杂度内解决问题。

T5 P12651 [KOI 2024 Round 2] 最大异或

“中等部第3题 / 高等部第2题. XOR最大值” 问题解答

作者:罗正辉

部分问题 1

由于 N ≤ 30,输入规模非常小,因此可以通过检查从 S 中选择 s₁ 和 s₂ 的所有 O(N⁴) 种情况来解决问题。

部分问题 2

介绍两种解法。

解法 1

众所周知,在由 N 个非负整数组成的数组 A 中,选择两个数使其 XOR 值最大化的问题,可以使用 Trie 数据结构在 O(N log(max Aᵢ)) 时间内解决。

本题是从 S 的 O(N²) 个子串中选择 2 个使其 XOR 值最大化的问题,由于每个子串的长度不超过 N,因此可以使用 Trie 在 O(N³) 时间内解决。

解法 2

不失一般性,假设 |s₁| ≥ |s₂|。同时,设 S 中最早出现的 '1' 的位置为 x(索引从 1 开始)。

观察 1: 只需考虑 s₁ 是 S 的前缀的情况。

证明: 假设存在一个答案,其中 s₁ 不是 S 的前缀。如果 s₁ 紧邻的前一个字符是 '1',则可以将 s₁ 向前扩展一位以增加其值,导致矛盾;如果 s₁ 紧邻的前一个字符是 '0',则向前扩展一位不会改变答案。因此,如果 s₁ 不是 S 的前缀,可以将其扩展到成为前缀为止。

观察 2: 两个子串 XOR 后,无法得到比从后往前数第 N - x + 1 位更高的位。

证明: 要得到第 t ≥ N - x + 2 位为 1,需要在 S[1···x-1] 之间存在 '1',但根据 x 的定义,在 S[x] 之前不可能存在 '1'。

观察 3: 只需考虑 s₁ 包含 S[x···N] 的形式。

证明: 需要证明如果 s₁ 不包含 S[x···N],则无法使第 N - x + 1 位为 1,证明过程与观察 2 类似。

综合以上三个观察,可以得到以下结论:

定理 1: 存在 s₁ = S 的答案。

证明: 根据观察 3,存在 s₁ 是 S 的后缀的答案。又根据观察 1,将 s₁ 向前扩展不会使答案减小。因此,存在 s₁ = S 的答案。

遍历 S 的所有子串 s₂,计算 S 与 s₂ 的 XOR 值并取最大值即可。这种情况下,可能的 s₂ 子串有 O(N²) 个,每次 XOR 操作需要 O(N) 时间。因此,可以在 O(N³) 时间内解决问题。

部分问题 3

介绍两种解法。

解法 1

使用 Trie 优化部分问题 2 的第二种解法,可以在 O(N²) 时间内解决。

解法 2

为方便起见,只考虑 S 中至少出现一个 '0' 和一个 '1' 的情况。如果 S 中没有 '1',则答案是 0;如果没有 '0',则答案是将 S 的最后一位改为 '0'。

设 S 中最早出现的 '1' 的位置以及连续的 '1' 的个数为 C,紧接着出现的 '0' 的位置为 P。

观察 4: 存在一个答案,其中 s₂ 的第一个字符与 S = s₁ 的第 P 个字符进行 XOR 运算。

证明: S[P] 是 '0',并且它的前面(S[P-1])是 '1',因此存在方法使得 XOR 结果的第 P 位为 '1',并且无法在不设置该位为 '1' 的情况下得到更好的答案。此外,容易知道,即使 s₂ 的第一个字符与比 S[P] 更靠前的字符匹配,答案也不会增加。

利用这个观察,可以将 s₂ 的候选数量减少到 C 个。例如,如果 S = 011110ABCDE...,那么 S 与 s₂ 的匹配方式只有以下四种:

S  = 011110ABCDE...
s2 =     11110A...
s2 =      1110AB...
s2 =       110ABC...
s2 =        10ABCD...

像这样,s₂ 的候选只有 C(≤ N) 个。实际需要考虑的 s₂ 只有 O(N) 种,因此可以在 O(N²) 时间内求得答案。

部分问题 4

将采用类似部分问题 3 的第二种解法的方式,即寻找与 S[P...N] 进行 XOR 后值最大的 s₂。

给定 A,最大化 A ⊙ B 等价于最小化 notA ⊙ B。寻找这样的 B 的问题,其答案是在所有可能的 B 的候选中,不大于 notA 的最大数,或者不小于 notA 的最小数。因此,如果能在 O(T(N)) 时间内比较子串的字典序,那么除了预处理时间外,可以在 O(N × T(N)) 时间内解决整个问题。

使用字符串 A#notA 的后缀数组,可以在 O(N log N) 时间内完成预处理,并在 O(1) 时间内比较子串的字典序;使用字符串哈希和二分查找,可以在 O(N) 时间内完成预处理,并在 O(log N) 时间内比较子串的字典序。两种方法的总时间复杂度都是 O(N log N)。

部分问题 5

将优化部分问题 3 的第二种解法。假设 S = 011110ABCDE...。

S  = 011110ABCDE...
s2 =     11110A...
s2 =      1110AB...
s2 =       110ABC...
s2 =        10ABCD...

如果 A = '1',则可以确定 s₂ = 10ABCD...;如果 A ≠ '1' 且 B = '1',则可以确定 s₂ = 110ABC...。

以此类推,设 P 之后首次出现的 '1' 的位置为 P + i,则可以确定 s₂ 是从 P - min(C, i) 位置开始的字符串。因此,可以在 O(N) 时间内找到作为答案的 s₂,从而在 O(N) 时间内解决整个问题。

区间覆盖问题的贪心模型详解

avatar lxn 2025-12-01 12:57:48

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-10-10 08:22:33

1. 区间覆盖问题的基本模型

模型一:选择不相交区间(最大不相交区间数)

问题描述:给定n个区间,选择尽可能多的区间,使得这些区间互不相交。

贪心策略:按区间的右端点从小到大排序

算法步骤:

1. 将所有区间按右端点从小到大排序
2. 初始化:count = 0, last_end = -∞
3. 遍历每个区间[l, r]:
   - 如果 l >= last_end:
     - count++
     - last_end = r
4. 返回count

证明思路:

  • 选择右端点最小的区间可以为后续留下更多空间
  • 任何最优解都可以通过替换变成贪心解

习题:

P1803 凌乱的yyy / 线段覆盖

模型二:区间完全覆盖

问题描述:用最少的区间覆盖指定的线段[L, R]

贪心策略:按左端点排序,每次选择能覆盖当前起点且右端点最大的区间

算法步骤:

1. 按左端点从小到大排序
2. 初始化:count = 0, current = L, i = 0
3. while current < R:
   - 在左端点<=current的区间中,选择右端点最大的
   - 如果找不到这样的区间:返回-1(无法覆盖)
   - count++, current = max_right
   - 如果current >= R: 返回count

习题:

  • [POJ 2376] Cleaning Shifts UVa 10382 /LOJ10002 https://loj.ac/p/10002

模型三:区间点覆盖(用最少的点覆盖所有区间)

问题描述:用最少的点,使得每个区间至少包含一个点

贪心策略:按右端点排序,在右端点处放点

算法步骤:

1. 按右端点从小到大排序
2. 初始化:count = 0, point = -∞
3. 遍历每个区间[l, r]:
   - 如果 l > point:
     - count++
     - point = r
4. 返回count

证明思路:

  • 在右端点放点可以覆盖尽可能多的后续区间

习题:

  • [LeetCode 452] 用最少数量的箭引爆气球
  • [USACO] 区间点覆盖

例题代码:

\/\/ LeetCode 452. 用最少数量的箭引爆气球
class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if (points.empty()) return 0;
        
        sort(points.begin(), points.end(), 
             [](const vector<int>& a, const vector<int>& b) {
                 return a[1] < b[1];
             });
        
        int arrows = 1;
        int arrow_pos = points[0][1];
        
        for (int i = 1; i < points.size(); i++) {
            if (points[i][0] > arrow_pos) {
                arrows++;
                arrow_pos = points[i][1];
            }
        }
        
        return arrows;
    }
};

模型四:区间分组(最小分组数)

问题描述:将区间分成若干组,每组内区间互不相交,求最少组数

贪心策略:按左端点排序,用小根堆维护每组的最大右端点

算法步骤:

1. 按左端点从小到大排序
2. 初始化小根堆(存储每组的最大右端点)
3. 遍历每个区间[l, r]:
   - 如果堆为空或堆顶 > l:
     - 新开一组,将r加入堆
   - 否则:
     - 更新堆顶为r(将该区间加入堆顶对应的组)
4. 返回堆的大小

习题: P2859 [USACO06FEB] Stall Reservations S

  • P7913 [CSP-S 2021] 廊桥分配
  • [LeetCode 253] 会议室 II

2. 经典竞赛真题

真题1:[POJ 1328] Radar Installation

题目:在x轴上放置雷达,每个雷达覆盖半径为d,求覆盖所有岛屿的最少雷达数

转换:将岛屿位置转换为x轴上的覆盖区间

对于岛屿(x,y),雷达能覆盖的x轴区间为:
[x - sqrt(d*d - y*y), x + sqrt(d*d - y*y)]

解法:区间点覆盖模型

真题2:P2859 [USACO06FEB] Stall Reservations S

题目:n头奶牛,每头有挤奶时间区间,求最少牛栏数

解法:区间分组模型

3. 进阶变形问题

变形1:带权区间调度

问题:每个区间有权值,选择不相交区间使总权值最大

解法:DP + 二分查找,不是纯贪心

  • P2439 [SDOI2005] 阶梯教室设备利用

变形2:区间合并

问题:合并所有重叠区间 解法:按左端点排序后合并

  • P2082 区间覆盖(加强版)

4. 练习题分类

基础练习

  1. 选择不相交区间

    • [LeetCode 435] 无重叠区间
    • [HDU 2037] 今年暑假不AC
  2. 区间完全覆盖

    • [POJ 2376] Cleaning Shifts
    • [LeetCode 1326] 灌溉花园的最少水龙头数目
  3. 区间点覆盖

    • [LeetCode 452] 用最少数量的箭引爆气球
    • [POJ 1328] Radar Installation
  4. 区间分组

    • [LeetCode 253] 会议室 II
    • [POJ 3190] Stall Reservations
    • AT_abc377_dMany Segments 2

进阶练习

  1. [Codeforces 1029C] Maximal Intersection

    • 删除一个区间使剩余区间交集最大
  2. [Atcoder ABC 103D] Robot Arms

    • 区间点覆盖的变形
  3. [USACO 2014 Jan] Ski Course Rating

    • 并查集+贪心思想
  4. [NOIP 2005] 校门外的树

    • 区间合并应用

5. 解题技巧总结

排序选择技巧

  • 选择不相交区间:按右端点排序
  • 区间完全覆盖:按左端点排序
  • 区间点覆盖:按右端点排序
  • 区间分组:按左端点排序

证明方法

  1. 交换论证:证明贪心选择不会比最优解差
  2. 归纳法:证明前k步都是最优的
  3. 反证法:假设存在更优解,推导矛盾

调试技巧

  • 画图理解区间关系
  • 构造边界测试用例
  • 与暴力解法对拍

通过系统练习这些模型,能够快速识别区间覆盖问题的类型并选择正确的贪心策略。记住核心思想:排序是贪心的前提,正确的排序方式决定了贪心策略的有效性。

P2239 [NOIP2014 普及组] 螺旋矩阵 讲解

avatar lxn 2025-12-01 12:57:48

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-15 14:39:52

转载:https://www.luogu.com.cn/article/ur0hlq2a

P2239 [NOIP2014 普及组] 螺旋矩阵 讲解

题目 数学题。

$50$ 分做法:

先暴力做出这个 $n\times n$ 的矩阵,最后直接输出。 时间复杂度为 $O(n^2)$,$n\in[1,30000]$,超时,考虑优化。

$O(n)$ 做法:

我们找出了所有的 $a_{i,j}$,但只输出了一个,浪费了大量时间。 参考以下蛇形方阵: $$\begin{bmatrix}1&2&3&4&5\&17&18&19&6\ &24&25&20&7\ &23&22&21&8\ &12&11&10&9\end{bmatrix}$$ 直接推式子有些难推,我们分类讨论:

  • $i=1,j\in(1,n):a_{i,j}=j$
  • $i\in(1,n),j=n:a_{i,j}=n+i-1$
  • $i=n,j\in(1,n):a_{i,j}=3n-j-1$
  • $i\in(1,n),j=1:a_{i,j}=4n-i-2$

不是以上情况怎么办呢? 我们给矩阵缩小一下: $$\begin{bmatrix}1&2&3\\8&9&4\&6&5\end{bmatrix}$$ 可以看到,中间的九个元素都减少了 $16$,也就是 $4(n-1)$。 那么设这个函数为 $f(n,i,j)$,最后一种情况为:

  • $i\in(1,n),j\in(1,n):f(n-2,i-1,j-1)+4(n-1)$ 总体复杂度为 $O(n)$,可以通过。 代码:
#include<bits\/stdc++.h>
#define int long long
#define inl inline
#define INF 0x2a2a2a2a
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define rer(i,x,y,cmp) for(int i=x;i<=y&&cmp;++i)
#define per(i,x,y) for(int i=x;i>=y;--i)
#define FAST ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int n,i,j;
inl int find(int n,int i,int j){
	if(i==1) return j;
	if(j==n) return n+i-1;
	if(i==n) return (n<<1)+n-j-1;
	if(j==1) return (n<<2)-i-2;
	return find(n-2,i-1,j-1)+((n-1)<<2);
}
signed main(){
	FAST;
	cin>>n>>i>>j;
	cout<<find(n,i,j);
	return 0;
}

$O(1)$ 做法:

虽说已经可以通过此题,但如果 $n$ 的规模再大一些,达到 $n\in[1,10^9]$,那就必须 $O(1)$ 解决了。 我们找一个更大的矩阵: $$\begin{bmatrix}1&2&3&4&5&6&7&8\8&29&30&31&32&33&34&9\&48&49&50&51&52&35&10\&47&60&61&62&53&36&11\&46&59&64&63&54&37&12\&45&58&57&56&55&38&13\&44&43&42&41&40&39&14\&21&20&19&18&17&16&15\end{bmatrix}$$ 一轮缩小,减少了 $4\times(8-1)=28$。 二轮缩小,减少了 $4\times(6-1)=20$。 三轮缩小,减少了 $4\times(4-1)=12$。 $b=\{28,20,12\}$,翻转一下变成 $b'=\{12,20,28\}$,可以看出这是一个等差数列。 很明显会缩小 $x=min(i,j,n-i+1,n-j+1)$ 次。 $b'$ 的末项是 $4(n-1)$,公差是 $8$,首项就是 $4(n-1)-8(x-2)$,得 $b_{sum}$: $$b_{sum}=\frac{(4(n-1)-8(x-2)+4(n-1))\times(x-1)}{2}$$ 总体复杂度为 $O(1)$。 代码:

#include<bits\/stdc++.h>
#define int long long
#define inl inline
#define INF 0x2a2a2a2a
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define rer(i,x,y,cmp) for(int i=x;i<=y&&cmp;++i)
#define per(i,x,y) for(int i=x;i>=y;--i)
#define FAST ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int n,i,j,x,y;
signed main(){
	FAST;
	cin>>n>>i>>j;
	x=min(min(i,n-i+1),min(j,n-j+1));
	y=((((n-((x-2)<<1)-1)<<2)+((n-1)<<2))*(x-1))>>1;
	i-=x-1;
	j-=x-1;
	n-=((x-1)<<1);
	if(i==1) cout<<y+j;
	else if(n-j+1==1) cout<<y+n+i-1;
	else if(n-i+1==1) cout<<y+(n<<1)+n-j-1;
	else cout<<y+(n<<2)-i-2;
	return 0;
}

vector、list 和 deque对比-deepseek学案

avatar lxn 2025-12-01 12:57:48

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-09 08:02:08

在 C++ STL 中,vector、list 和 deque 是三种常用的顺序容器,它们的相同点与不同点对比如下:


相同点

  1. 动态容器
    均支持动态扩容/缩容,无需手动管理内存,容量随元素增减自动变化。
  2. 顺序存储
    元素按插入顺序存储(逻辑连续),支持顺序遍历。
  3. 迭代器支持
    提供随机访问迭代器(vector、deque)或双向迭代器(list),支持 begin()/end() 遍历。
  4. 通用操作
    支持 push_back()、pop_back()、insert()、erase()、size()、empty() 等基本操作。

不同点

特性 vector list deque
底层结构 动态数组 双向链表 分块数组(多个连续块)
随机访问 ✅ O(1) ❌ O(n) ✅ O(1)(接近 vector)
中间插入/删除 ❌ O(n)(需移动元素) ✅ O(1)(已知迭代器位置) ❌ O(n)(需移动元素)
头尾插入/删除 ✅ 尾端 O(1),头端 O(n) ✅ 头尾 O(1) ✅ 头尾 O(1)
内存连续性 ✅ 完全连续 ❌ 非连续(节点分散) ❌ 逻辑连续,物理分块
内存分配策略 扩容时重新分配整个数组 按需分配单个节点 按需分配内存块(无需全拷贝)
迭代器失效场景 扩容后全部失效 删除时仅当前元素迭代器失效 中间插入/删除可能导致失效
内存占用 最低(仅元素内存) 较高(含前后指针) 中等(含块指针表)

核心差异总结

  1. 访问模式

    • vector:适合频繁随机访问(如 v[i])和尾部操作。
    • deque:适合频繁头尾插入/删除,随机访问性能接近 vector。
    • list:适合频繁中间插入/删除,但无法快速随机访问。
  2. 内存效率

    • vector:内存紧凑,缓存友好,但扩容成本高。
    • list:内存碎片化,但无扩容开销。
    • deque:折中设计,兼顾头尾操作和随机访问。
  3. 迭代器稳定性

    • list 的迭代器最稳定(除非元素被删除)。
    • vector 扩容后所有迭代器失效。
    • deque 在中间修改时可能局部失效。

典型应用场景

  • vector:需要快速随机访问(如数值计算、动态数组)。
  • list:需要频繁中间插入/删除(如 LRU 缓存链表)。
  • deque:需要高效头尾操作(如任务队列、滑动窗口)。

根据具体需求选择容器,可显著优化程序性能和内存效率。

map\/multimap-deepseek 学案

avatar lxn 2025-12-01 12:57:48

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-08 21:12:30

map/multimap运用

一、容器特性对比

特性 map multimap
键唯一性 唯一 允许重复键
底层实现 红黑树 红黑树
插入复杂度 O(log n) O(log n)
查找复杂度 O(log n) O(log n)
迭代器类型 双向迭代器 双向迭代器
典型应用场景 字典、哈希表替代品 一对多关系存储

二、核心操作详解

1. 基本操作

#include <map>
using namespace std;

map<string, int> mp;
multimap<int, string> mmp;

\/\/ 插入元素
mp["Alice"] = 95;                   \/\/ 直接赋值
mp.insert({"Bob", 88});             \/\/ 插入pair对象
mmp.emplace(90, "Charlie");         \/\/ 原地构造

\/\/ 删除元素
mp.erase("Alice");                  \/\/ 按key删除
mmp.erase(mmp.find(90));            \/\/ 按迭代器删除

\/\/ 查找操作
auto it = mp.find("Bob");           \/\/ 返回迭代器
int cnt = mmp.count(90);            \/\/ 统计key出现次数

\/\/ 范围迭代
auto [start, end] = mmp.equal_range(85); \/\/ 获取键值范围

2. 高级操作

\/\/ 自定义排序规则
struct cmp {
    bool operator()(const string& a, const string& b) const {
        return a.length() < b.length();
    }
};
map<string, int, cmp> lenMap;

\/\/ 合并map(C++17)
map<int, int> m1, m2;
m1.merge(m2);  \/\/ 合并后m2保留冲突键值

\/\/ 节点操作(C++17)
auto nh = mp.extract("Alice");      \/\/ 提取节点
mp.insert(move(nh));                \/\/ 重新插入

三、经典例题解析

例题1:POJ2503 Babelfish(字典查询)

题目要求:实现双语词典的单词翻译功能

map实现:

#include <iostream>
#include <map>
#include <sstream>
using namespace std;

map<string, string> dict;

int main() {
    string line, en, foreign;
    while(getline(cin, line) && !line.empty()) {
        istringstream iss(line);
        iss >> en >> foreign;
        dict[foreign] = en;
    }
    
    while(cin >> foreign) {
        auto it = dict.find(foreign);
        if(it != dict.end()) 
            cout << it->second << endl;
        else 
            cout << "eh" << endl;
    }
    return 0;
}

例题2:洛谷P3374 【模板】树状数组 1(map模拟解法)

题目要求:实现单点修改、前缀求和操作

map优化解法:

#include <iostream>
#include <map>
using namespace std;

map<int, int> tree;  \/\/ 离散化存储非零位置

void update(int pos, int val) {
    while(pos <= n) {
        tree[pos] += val;
        pos += pos & -pos;
    }
}

int query(int pos) {
    int sum = 0;
    while(pos > 0) {
        sum += tree[pos];
        pos -= pos & -pos;
    }
    return sum;
}

例题3:AtCoder ABC245D - Polynomial division(多项式除法)

题目要求:给定多项式A(x)*B(x)=C(x),已知A和C求B

map存储系数解法:

#include <bits\/stdc++.h>
using namespace std;

map<int, int> read_poly(int n) {
    map<int, int> res;
    for(int i=0; i<=n; ++i) {
        int c; cin >> c;
        if(c != 0) res[i] = c;
    }
    return res;
}

int main() {
    int n, m;
    cin >> n >> m;
    auto A = read_poly(n);
    auto C = read_poly(n+m);
    
    map<int, int> B;
    for(int k=m; k>=0; --k) {
        int sum = 0;
        for(auto [i,a] : A) {
            if(B.find(k-i) != B.end())
                sum += a * B[k-i];
        }
        B[k] = (C[k] - sum) \/ A.begin()->second;
    }
    \/\/ ...输出B的系数...
    return 0;
}

四、进阶应用技巧

1. 离散化坐标压缩

vector<int> coords = {100, 5, 200, 5, 100};
map<int, int> mapping;
int idx = 1;
for(auto x : coords)
    if(!mapping.count(x))
        mapping[x] = idx++;

2. 时间线管理(扫描线算法)

map<int, int> timeline;  \/\/ 时间戳 -> 事件数量

void add_event(int t) {
    timeline[t]++;
}

void process_events() {
    auto it = timeline.begin();
    while(it != timeline.end()) {
        int t = it->first;
        int cnt = it->second;
        \/\/ 处理t时刻的cnt个事件
        it = timeline.erase(it);
    }
}

五、练习题推荐

  1. 洛谷P1276 校门外的树(增强版)(区间状态维护)
  2. POJ3481 Double Queue(双优先队列模拟)
  3. AtCoder ABC217D - Cutting Woods(坐标离散化)
  4. 洛谷P4056 [JSOI2009] 火星藏宝图(动态规划状态优化)
  5. Codeforces 4C - Registration System(用户名计数)

六、常见错误与优化

典型错误案例

\/\/ 错误1:直接访问不存在的键
map<string, int> mp;
cout << mp["unknown"];  \/\/ 自动插入默认值0

\/\/ 正确做法
if(mp.count("unknown")) 
    cout << mp["unknown"];

\/\/ 错误2:错误删除范围
multimap<int, int> mmp;
mmp.erase(5);  \/\/ 删除所有key=5的元素

\/\/ 正确删除单个元素
auto it = mmp.find(5);
if(it != mmp.end()) mmp.erase(it);

性能优化技巧

  1. 预分配内存(适用于已知规模):
map<int, int> mp;
mp.reserve(1e5);  \/\/ C++23+支持
  1. 批量插入优化:
vector<pair<int, int>> data{{1,2},{3,4}};
mp.insert(data.begin(), data.end());
  1. 利用有序性进行范围统计:
\/\/ 统计[key1, key2)区间的value总和
auto it1 = mp.lower_bound(key1);
auto it2 = mp.lower_bound(key2);
int sum = accumulate(it1, it2, 0, 
    [](int s, auto& p){ return s + p.second; });
  1. 使用emplace替代insert:
mp.emplace(5, 10);  \/\/ 避免临时对象构造

set\/multiset运用-deepseek写学案

avatar lxn 2025-12-01 12:57:48

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-05-08 20:53:26

set/multiset运用

一、容器特性对比

特性 set multiset
元素唯一性 唯一 允许重复
底层实现 红黑树 红黑树
插入复杂度 O(log n) O(log n)
查找复杂度 O(log n) O(log n)
迭代器类型 双向迭代器 双向迭代器
常用场景 维护唯一有序集合 允许重复的有序集合

二、核心操作详解

1. 基础操作

#include <set>
using namespace std;

set<int> s;
multiset<int> ms;

\/\/ 插入元素
s.insert(5);       \/\/ set返回pair<iterator, bool>
ms.insert(5);      \/\/ multiset返回iterator

\/\/ 删除元素
s.erase(5);        \/\/ 删除所有值为5的元素
ms.erase(ms.find(5)); \/\/ 仅删除第一个找到的5

\/\/ 查找操作
auto it = s.find(3);   \/\/ 找不到返回s.end()
int cnt = ms.count(5); \/\/ 统计元素出现次数

\/\/ 范围查找
auto [low, high] = s.equal_range(4); \/\/ 获取等于4的元素区间

\/\/ 特殊访问
s.begin();        \/\/ 最小元素
s.rbegin();       \/\/ 最大元素(反向迭代器)

2. 高级操作

\/\/ 合并集合(C++17)
set<int> s1, s2;
s1.merge(s2);  \/\/ 将s2元素移动到s1,重复元素保留在s2

\/\/ 提取节点(C++17)
auto nh = s.extract(3); \/\/ 获取节点但不复制
s.insert(move(nh));     \/\/ 重新插入

\/\/ 自定义排序规则
struct cmp {
    bool operator()(const string& a, const string& b) const {
        return a.length() < b.length();
    }
};
set<string, cmp> lengthSet;

三、经典例题解析

例题1:洛谷P1177 【模板】排序(去重排序)

题目要求:对n个数字进行升序排序并去重

set实现:

#include <iostream>
#include <set>
using namespace std;

int main() {
    int n, x;
    set<int> s;
    cin >> n;
    while(n--) {
        cin >> x;
        s.insert(x);
    }
    cout << s.size() << endl;
    for(auto v : s) cout << v << " ";
    return 0;
}

例题2:AtCoder ABC212C - Min Difference

题目要求:在两个数组中找到最小差值

multiset优化解法:

#include <bits\/stdc++.h>
using namespace std;

int main() {
    int n, m, x;
    multiset<int> s;
    
    cin >> n >> m;
    for(int i=0; i<n; ++i) {
        cin >> x;
        s.insert(x);
    }
    
    int ans = INT_MAX;
    while(m--) {
        cin >> x;
        auto it = s.lower_bound(x);
        if(it != s.end()) ans = min(ans, *it - x);
        if(it != s.begin()) ans = min(ans, x - *prev(it));
    }
    cout << ans << endl;
    return 0;
}

例题3:洛谷P3369 【模板】普通平衡树

功能需求:

  1. 插入x
  2. 删除x(若有多个相同元素,只删除一个)
  3. 查询x的排名
  4. 查询排名为x的数
  5. 求x的前驱
  6. 求x的后继

multiset实现:

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;

multiset<int> ms;

int get_rank(int x) {
    return distance(ms.begin(), ms.lower_bound(x)) + 1;
}

int get_num(int k) {
    auto it = ms.begin();
    advance(it, k-1);
    return *it;
}

int main() {
    int q, op, x;
    cin >> q;
    while(q--) {
        cin >> op >> x;
        switch(op) {
        case 1: ms.insert(x); break;
        case 2: ms.erase(ms.find(x)); break;
        case 3: cout << get_rank(x) << endl; break;
        case 4: cout << get_num(x) << endl; break;
        case 5: 
            auto it = ms.lower_bound(x);
            cout << *(--it) << endl;
            break;
        case 6:
            cout << *ms.upper_bound(x) << endl;
            break;
        }
    }
    return 0;
}

四、进阶应用技巧

1. 维护动态中位数

https://www.luogu.com.cn/problem/U207461

题目要求:依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。

multiset<int> left, right; \/\/ 左半部大顶堆,右半部小顶堆

void insert(int x) {
    if(left.empty() || x <= *left.rbegin()) 
        left.insert(x);
    else 
        right.insert(x);
    
    \/\/ 平衡两个集合
    if(left.size() > right.size()+1) {
        right.insert(*left.rbegin());
        left.erase(prev(left.end()));
    } else if(right.size() > left.size()) {
        left.insert(*right.begin());
        right.erase(right.begin());
    }
}

\/\/ 获取中位数
int get_median() {
    return *left.rbegin();
}

2. 离散化处理

\/\/ 使用set进行离散化
vector<int> arr = {5, 3, 9, 3, 7};
set<int> s(arr.begin(), arr.end());
unordered_map<int, int> mapping;
int idx = 1;
for(int v : s) mapping[v] = idx++;

五、练习题推荐

  1. 洛谷P1059 明明的随机数(set去重排序)
  2. 洛谷P2073 送花(multiset维护双关键字)
  3. AtCoder ABC245D - Polynomial division(multiset维护多项式系数)
  4. 洛谷P2286 [HNOI2004]宠物收养所(平衡树应用)
  5. 洛谷U207461 动态维护中位数(用set实现对顶堆)
  6. Codeforces 706D - Vasiliy's Multiset(异或操作+multiset维护)

六、常见错误与优化

典型错误案例

\/\/ 错误1:迭代器失效
set<int> s = {1,2,3};
for(auto it=s.begin(); it!=s.end(); ++it) {
    if(*it == 2) s.erase(it); \/\/ erase后it失效
}

\/\/ 正确写法
auto it = s.find(2);
if(it != s.end()) s.erase(it);

\/\/ 错误2:错误删除所有元素
multiset<int> ms = {1,1,2};
ms.erase(1); \/\/ 删除所有1,留下size=1

\/\/ 正确删除单个元素
ms.erase(ms.find(1));

性能优化技巧

  1. 使用emplace_hint优化插入:
auto hint = s.end();
for(int x : data) {
    hint = s.emplace_hint(hint, x);
}
  1. 批量操作优化:
vector<int> temp{5,2,8,3};
s.insert(temp.begin(), temp.end()); \/\/ 优于循环插入
  1. 利用有序性进行范围查询:
\/\/ 统计[L, R]区间内的元素数量
int count = distance(s.lower_bound(L), s.upper_bound(R));
lxn Avatar
活跃度 近 53 周
查看全年