241111 noip 题解

news/2024/11/13 0:16:12/文章来源:https://www.cnblogs.com/System-Error/p/18540229

省流:\(100+50+10+30\)。还是不稳定啊,noip上不了270就真的要退役了。

T1

题意:给定一个长度为 \(n\) 的序列 \(a\),每次你可以交换相邻两个位置,求出最小交换次数以及字典序最小的交换方案使得 \(a\) 的每个不是本身的前缀都不是排列。

\(n \leq 10^5\)

注意到每次交换至多会减少一个是排列的前缀。因此最少操作次数就是这样的前缀个数。

至于最小字典序,直接从前往后操作即可。

时间复杂度 \(\Theta(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N];
vector<int> ve;
int main() {ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>n;for(int i=1; i<=n; i++) cin>>a[i];int mx=0,mn=n+1;for(int i=1; i<n; i++) {if(min(mn,a[i])==1&&max(mx,a[i])==i) ve.push_back(i),swap(a[i],a[i+1]);mn=min(mn,a[i]),mx=max(mx,a[i]);}cout<<ve.size()<<'\n';for(int i=0; i<ve.size(); i++) cout<<ve[i]<<" ";return 0;
}

T2

题意:给定一个 \(n \times m\) 的网格,你需要操作一个角色走出网格。每个格子上有一个给定的方向,当角色站在这个格子上时,可以向格子给定的方向走不超过 \(k\) 步,求有多少个格子可以作为出发点让角色走出网格。

\(k \leq n,m \leq 3000\)

考虑倒着做,如果一个格子可以走出网格,那么其余能够通过走一步到达这个格子的格子也能走出。于是我们可以对于每个格子与它上面第一个 D,下面第一个 U,左边第一个 R,右边第一个 L 连边,前提是距离这个格子不超过 \(k\),然后把可以一步走出网格的格子扔进队列跑多源 bfs 即可。容易证明这样一定能遍历到所有能够走出网格的格子。

时间复杂度 \(\Theta(nm)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3005;
char ch[N][N];
int n,m,k,vis[N][N],head[N][N],ecnt=0;
struct edge {int tox,toy,nxt;}e[N*N<<2];
inline void add(int x,int y,int xx,int yy) {e[++ecnt]=(edge){xx,yy,head[x][y]};head[x][y]=ecnt;}
inline bool check(int x,int y) {if(ch[x][y]=='D') if(x+k>n) return true;if(ch[x][y]=='U') if(x-k<1) return true;if(ch[x][y]=='L') if(y-k<1) return true;if(ch[x][y]=='R') if(y+k>m) return true;return false;
}
int main() {ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>n>>m>>k;for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) cin>>ch[i][j];for(int i=1; i<=n; i++) {int lst=0;for(int j=1; j<=m; j++) {if(lst&&j-lst<=k) add(i,j,i,lst);if(ch[i][j]=='R') lst=j;}}for(int i=1; i<=n; i++) {int lst=0;for(int j=m; j>=1; j--) {if(lst&&lst-j<=k) add(i,j,i,lst);if(ch[i][j]=='L') lst=j;}}for(int i=1; i<=m; i++) {int lst=0;for(int j=1; j<=n; j++) {if(lst&&j-lst<=k) add(j,i,lst,i);if(ch[j][i]=='D') lst=j;}}for(int i=1; i<=m; i++) {int lst=0;for(int j=n; j>=1; j--) {if(lst&&lst-j<=k) add(j,i,lst,i);if(ch[j][i]=='U') lst=j;}}queue<pair<int,int>> q;for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if(check(i,j)) q.push(make_pair(i,j)),vis[i][j]=true;while(!q.empty()) {int x=q.front().first,y=q.front().second;q.pop();for(int i=head[x][y]; i; i=e[i].nxt) {int vx=e[i].tox,vy=e[i].toy;if(vis[vx][vy]) continue;vis[vx][vy]=true;q.push(make_pair(vx,vy));}}int ans=0;for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) ans+=vis[i][j];cout<<ans;return 0;
}

T3

题意:你需要求出满足以下条件的长度为 \(n + 2\)\(a\) 序列的数量。

  • \(a_0 = a_{n + 1} = 0\)
  • \(\forall i \in [1,n],a_i \geq 0\)
  • \(\sum_{i = 0}^n \max(a_{i + 1} - a_i,0) = m\)

\(1 \leq n,m \leq 2 \times 10^5\)

解法一:

注意到答案相当于:对每个长度为 \(2m\) 的合法括号序列,将其分为 \(n + 1\) 段(每段可以为空),要求每段内不能同时含有左右括号,求方案数之和。

发现一个括号序列的划分方案数只和其中 () 子串的个数有关,对于左括号相当于从 \((x,y)\) 走到 \((x,y+1)\),右括号相当于 \((x,y)\) 走到 \((x+1,y)\),画出格路,就是只和拐弯次数有关(注意这个拐弯是先向上再向右的拐弯)。不妨称 () 子串在格路中对应一个“拐点”,则要对每个 \(k\) 求从 \((0,0)\)\((m,m)\),恰好有 \(k\) 个拐点,且始终有 \(x \leq y\) 的格路个数。为什么是对的呢?因为我们知道了拐点数为 \(2k - 1\),这些拐点是已经钦定的 \(a_i\),所以还剩下 \(n - 2k + 1\) 个点没有钦定,因为是从 \((0,0)\)\((m,m)\),所以这些没钦定的点有 \(2m + 1\) 种选择,由于可重且点是相同的,所以钦定未钦定的点的方案是 \(C_{2m + n - 2k + 1}^{n - 2k + 1}\) 的。这是一个固定的值。

先考虑如果没有 \(x \leq y\) 的限制怎么做。只要在两个坐标上分别选 \(k\) 个拐点的位置,就唯一对应一条格路。显然方案数是 \((C_n^k)^2\)。这启发我们对“拐点坐标”构成的序列进行计数,而非对格路进行计数。

设拐点坐标为 \((x_1,y_1),(x_2,y_2),\cdots,(x_k,y_k)\)。于是我们要减去存在某个拐点满足 \(y_i < x_{i + 1}\) 的格路数,表示在第 \(i\) 个位置走到第 \(i + 1\) 个位置后不合法了。不妨假设 \(i\) 是第一个 \(y_i < x_{i + 1}\) 的拐点。那么这样的路径满足的充要条件是:

\[0 \leq x_1 < x_2 < \cdots < x_i < y_{i + 1} < y_{i + 2} < \cdots < y_k \leq n \]

\[0 < y_1 < y_2 < \cdots < y_i < x_{i + 1} < x_{i + 2} < \cdots < x_k < n \]

由于是格点,所以是小于。两维独立,所以对上述两个柿子分别计算方案数就行,这样的两个序列包含了所有 \(i\) 的情况。容易证明。

所以有 \(k\) 个拐点的方案数为:

\[(C_n^k \times C_n^k - C_{n + 1}^k \times C_{n - 1}^k) \times C_{2m + n - 2k + 1}^{n - 2k + 1} \]

对于每个 \(k\) 求和即可。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=7e5+5,p=1e9+7;
int n,m,ans=0,fac[N],inv[N];
int qpow(int a,int b) {int ans=1;while(b) {if(b&1) ans=ans*a%p;a=a*a%p;b>>=1;}return ans;
}
void init(int lim) {fac[0]=inv[0]=1;for(int i=1; i<=lim; i++) fac[i]=fac[i-1]*i%p;inv[lim]=qpow(fac[lim],p-2);for(int i=lim-1; i>=1; i--) inv[i]=inv[i+1]*(i+1)%p;
}
int C(int n,int m) {if(n<0||m<0||n<m) return 0;return fac[n]*inv[m]%p*inv[n-m]%p;
}
signed main() {cin>>n>>m;init(600005);for(int k=0; k<=n; k++) ans=(ans+(C(m,k)*C(m,k)%p-C(m+1,k)*C(m-1,k)%p+p)%p*C(2*m+n-2*k+1,n-2*k+1)%p)%p;cout<<ans;return 0;
}

解法二

\(a\) 序列进行差分,得到长度为 \(n + 1\) 的序列 \(b\)。限制变为 \(b\) 的任意前缀和都 \(\geq 0\),且 \(b\) 中所有数之和 \(= 0\),绝对值之和 \(= 2m + 1\)

\(b_1\) 改为 \(b_1 + 1\),限制变为 \(b\) 的任意前缀和都 \(> 0\),且 \(b\) 中所有数之和 \(= 1\),绝对值之和 \(= 2m + 1\)

  • Raney 引理:如果 \(x_1,x_2,\cdots,x_k\) 是一个和为 \(1\) 的整数序列,则其所有循环位移中恰好有一个满足所有的前缀和都是正数。

  • 证明:考虑重复这个序列,生成一个无限数列。这个数列的“平均斜率”是 \(\frac{1}{k}\),并且整个图象会被夹在两条斜率为 \(\frac{1}{k}\) 的直线之间,并且每条直线与图象恰好有一个切点(因为直线在每个周期内与整点只接触一次)。循环位移的起始点能且只能取下界直线的切点。

因此只需统计和为 \(1\) 且绝对值之和为 \(2m + 1\) 的序列个数,最后除以 \(n + 1\) 即可。枚举负数的个数 \(i\),方案数是:

\[C_{m - 1}^{i - 1} C_{m + 1 + n - i}^{n - i} C_{n + 1}^{i} \]

神奇的是,两种做法推出来的式子完全不一样,但却是相等的。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e5+5,p=1e9+7;
int n,m,ans=0,fac[N],inv[N];
int qpow(int a,int b) {int ans=1;while(b) {if(b&1) ans=ans*a%p;a=a*a%p;b>>=1;}return ans;
}
void init(int lim) {fac[0]=inv[0]=1;for(int i=1; i<=lim; i++) fac[i]=fac[i-1]*i%p;inv[lim]=qpow(fac[lim],p-2);for(int i=lim-1; i>=1; i--) inv[i]=inv[i+1]*(i+1)%p;
}
int C(int n,int m) {if(n<0||m<0||n<m) return 0;return fac[n]*inv[m]%p*inv[n-m]%p;
}
signed main() {cin>>n>>m;init(m+n+1);for(int i=0; i<=n+1; i++) ans=(ans+C(m-1,i-1)*C(m+1+n-i,n-i)%p*C(n+1,i)%p)%p;cout<<ans*qpow(n+1,p-2)%p;return 0;
}

闲话:感觉解法一更加值得学习一点,解法二的引理真的会考吗/oh/oh

T4

原题:P8415。

还不会。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/831886.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot项目引入Elasticsearch时启动失败

1、前情提要: https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/installation.html 以上是Elasticsearch对接Java的官方文档(pom依赖部分)我本地Windows安装的Elasticsearch也是8.15.3版本 2、启动报错 *************************** APPLICA…

四款超实用的免费报表工具推荐!轻松选择适合你的数据神器

现如今,报表工具逐渐成为企业数据分析和决策的重要一环。本文将为大家介绍四款免费报表工具,通过这些工具,用户可以轻松地将原始数据转换为直观易懂的报表,帮助决策者更快地获取信息和做出判断。无论是寻求简单易用、实时更新,还是需要强大的数据分析能力的用户,都能在这…

联训题单 / 集训杂题纪要

UPD: 新增了杂题选改栏 总览题单 进度 备注数据结构1 4/24 数据结构可爱捏 >_<搜索 模拟 All Clear/10 搜索可爱捏 >_<数学1 0/11 数学不可爱捏 `-字符串 6/13 哈希可爱捏 >_<杂题选改 7 杂题专题没了,杂题倒是有不少数据结构 1 STEP 读假题了,读成下面这…

Python 提取PowerPoint文档中的图片

如果你需要在多个PowerPoint演示文稿中使用相同的图片,直接从原始PPT中提取并保存图片可以避免重复寻找和下载。此外,将PPT中的重要图片提取出来可以将其作为备份,以防原文件损坏或丢失。本文将通过以下两个示例介绍如何使用Python提取PPT文档中的图片。Python 提取指定幻灯…

高级语言程序设计课程第七次个人作业

班级:https://edu.cnblogs.com/campus/fzu/2024C 作业要求:https://edu.cnblogs.com/campus/fzu/2024C/homework/13304 学号:102400121 姓名:林永庆 12 从左到右,从上到下,从右到左,从下到上345678总结:菜就多练 反思:菜就多练

解线性方程组迭代法

解线性方程组迭代法 在数值分析中,迭代法是解决大规模线性方程组的重要工具。迭代法可以有效地减少计算复杂度,使得求解效率更高。本文将从前置知识开始,介绍向量和矩阵的范数,再深入探讨求解线性方程组的 Jacobi 和 Gauss-Seidel 迭代法。 一、前置知识:向量和矩阵的范数…

Linux kernel 堆溢出利用方法(二)

本文我们通过我们的老朋友heap_bof来讲解Linux kernel中off-by-null的利用手法。在通过讲解另一道相对来说比较困难的kernel off-by-null + docker escape来深入了解这种漏洞的利用手法。前言 本文我们通过我们的老朋友heap_bof来讲解Linux kernel中off-by-null的利用手法。在通…

wpf项目使用winform控件

环境:Win10、VS2017 一、新建WPF项目 2. WPF项目添加System.Windows.Forms和WindowsFormsIntegration引用 3. 编写WPF窗体代码 3.1. 头部添加引用1 xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" 2 xmlns:wfi ="clr-namespac…

Spring带泛型的ApplicationEvent无法监听问题分析(转载)

1 背景 在开发过程中,经常遇到发送事件来通知其他模块进行相应的业务处理;笔者实用的是spring自带的ApplicationEventPublisher和EventListener进行事件的发收; 但是开发时遇到一个问题: 如果事件很多,但是事件模式都差不多,就需要定义很多事件类来分别表示各种事件,例如…

PG 修改表结构提示有试图依赖的处理方法

ALTER TABLE victim ALTER COLUMN victim_belong_url TYPE varchar(1000) USING victim_belong_url::varchar(1000); 修改字段长度 通过修改 pg_attribute 基表的方式来绕开这个限制#通过表名查出attrelid SELECT relname, attname,attnum,attrelid,attname FROM pg_class c,pg…

OMV安装文件管理器filebrowser和照片管理photoprism插件时Pull不了镜像的解决办法

OMV安装文件管理器filebrowser和照片管理photoprism插件安装后不能启动服务或者PULL不了镜像卡着不动都是因为现在国内pull不了镜像的原因 这里有个迷惑的人的地方是很多朋友认为是用docker来pull的镜像,于是增加了docker国内加速源后发现OMV还是拉取不了镜像。解决方法如下:…

NOIP2024加赛4

NOIP2024加赛4\(T1\) luogu P11267 【MX-S5-T1】王国边缘 \(85pts\)预处理前缀中最后一个 \(1\) 出现的位置然后就可以倍增跳了。点击查看代码 const ll p=1000000007; int nxt[200010][62],f[200010][62],last[200010]; char t[200010]; ll divide(ll s,ll k) {ll ans=0;for(l…