test20230109考试总结-2023寒搜索专题
前言
2023 年的第一篇考试总结……
赛时得分情况:
A | B | C | D | E | F | G | \(\texttt{Total}\) | \(\texttt{Rank}\) |
---|---|---|---|---|---|---|---|---|
\(40\) | \(80\) | \(0\) | \(0\) | \(100\) | \(100\) | \(0\) | \(320\) | \(6\) |
考试总结完善情况
A | B | C | D | E | F | G | \(\texttt{All}\) |
---|---|---|---|---|---|---|---|
√ | √ | √ | 没有题解 | √ | √ |
A. P1731 [NOI1999] 生日蛋糕
题面
你需要制作一个体积为 \(\pi N\) 的,\(M\) 层的,每层都是圆柱体的蛋糕。要求蛋糕从下至上的底面半径和高单调递减。
你需要求出满足以上条件的蛋糕中外表面积(就是每个蛋糕的侧面积与最上面的蛋糕的底面积的和)最小的方案。输出这个方案的蛋糕的外表面积除以 \(\pi\) 的值。如果无解请输出 \(0\)。
\(1 \leq N \leq 2 \times 10^4,1 \leq M \leq 15\)
题解
做法 1:我会暴搜!自下而上搜索,期望得分:\(0\sim20\operatorname{pts}\)。
做法 2:我会剪枝!预处理 \(L_i = \sum_{j=1}^{i}j^3\)。就是从上到下前 \(j\) 个蛋糕的最小体积和。然后如果当前体积 \(V\) 满足 \(V+L_{d}\gt n\)(\(d\) 为剩余层数),那么就可以减掉。枚举一层的高 \(H\) 时下界也可以改为 \(\dfrac{n-V-L_{d}}{R^2}\)(\(R\) 为枚举的半径)。结合上面的做法,期望得分 \(40\sim50\operatorname{pts}\)。
做法 3:我会剪枝!如体积减了枝 表面积也可以!
设第 \(i\) 层蛋糕的侧面积为 \(S_i\)。余下的为 \(F_i\)。
&2V_i = \sum_{j=i+1}^{M}{R_{j}^2H_{j}}\\
&=\sum_{j=i+1}^{M}{R_{i+1}S_{i+1}}\\
&\leq R_{i+1}\sum_{j=i+1}^{M}{S_j}\\
&=R_{i+1}+F_i
\end{aligned}
\]
所以就可以推出来了。(如果直接预处理可以得到 \(70\) 分)。
\(s+2(n-v)\div r\) 要大于等于目前的答案。
结合上面的做法,期望得分 \(70\sim100\operatorname{pts}\)。
注意倒序枚举 \(H,R\) 比正序快,原因待查。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
const int N = 2e4+5;
int lmin[N];
int result=INT_MAX;
void dfs(int v,int s,int dep,int r,int h){
// v:已用体积 s:已用表面积 dep:剩余层数 r:上一个蛋糕的半径 h:上一个蛋糕的高
// 自下而上搜索
if(dep==0){
if(v==n) result=min(result, s);
return;
}
if(v+lmin[dep-1]>n) return;
if(2*(n-v)/r+s>=result) return;
for(int R=r-1;R>=dep;R--){
if(dep==m) s=R*R;
for(int H=min((n-v-lmin[dep-1])/(R*R),h-1);H>=dep;H--){
// 剪枝:H的上界其实是 (期望体积-已用体积-剩余的最大体积)/底面积
dfs(v+R*R*H,s+H*(R<<1),dep-1,R,H);
}
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
lmin[i]=i*i*i+lmin[i-1];// lmin[i]:前 i 层的最大体积。
}
dfs(0,0,m,n+1,n+1);
if(result >= INT_MAX) result=0;
cout<<result;
return 0;
}
B. P1463 [POI2001][HAOI2007] 反素数
题面
定义:如果一个数 \(k\) 是反质数(Antiprime),当且仅当 \(\forall n(1 \leq n \lt k),g(k)\gt g(n)\)。其中 \(g(x)\) 为 \(x\) 的正因子个数。特别地,\(1\) 是反质数。
现在给定一个数 \(N\)。输出不超过 \(N\) 的最大的反质数。
\(1 \leq n \leq 2 \times 10^9\)。
题解
- 我会打表!以 \(O(n\sqrt{n})\) 的朴素暴力打表为例。大约 \(1\) 小时可以获得 \(80\) 分。大约 \(3\) 小时可以获得 \(100\) 分。
- 我会正解!
定理:对于一个反质数 \(p\),当且仅当 \(p=P_1^{e_1}P_2^{e_2}\cdots P_{k}^{e_k}\)。满足 \(P_1,P_2,\cdots P_k\) 依次为前 \(k\) 个质数,且 \(e_1\sim e_k\) 单调不增。
\(\mathcal{Proof}\):若 \(p\) 是一个不满足上命题的反质数,则将 \(e\) 从大到小排序后,一定存在另一个反质数 \(q=2^{e_1}3^{e_2}5^{e_3}\cdots\)。且 \(p>q,g(p)=g(q)\),违背反质数定义,因此 \(p\) 一定不是反质数。原命题得证。
因为 \(2\times3\times5\times7\times11\times13\times17\times19\times21\times23\times29\times31=4211770292730\gt2\times10^9\)。所以最多只需要取 \(k=11\)。
又因为 \(2^{31}=2147483648>2\times10^9\),所以 \(e\) 最大只需要取 \(31\)。
然后我们考虑暴搜即可。实际上不用加剪枝跑得飞快。
代码
#include <bits/stdc++.h>
using namespace std;
vector<int> prime={0,2,3,5,7,11,13,17,19,21,23,29,31};
int n,maxg,maxv;
void dfs(long long v,int g,int pri,int exp){
if((maxv>v&&maxg==g)||g>maxg){
maxg=g,maxv=v;
}
for(int j=1;j<=exp;j++){
v*=prime[pri];
if(v>n) break;
dfs(v,g*(j+1),pri+1,j);
}
}
signed main(){
cin>>n;
dfs(1,1,1,31);
cout<<maxv;
return 0;
}
C. P1985 [USACO07OPEN]翻转棋 Fliptile S
题面
给出一个 \(M\times N\) 的 \(01\) 矩阵。你可以进行任意次的操作。
每次操作你可以选择一个元素 \((i,j)\)。将其本身和与之相邻的元素取反。
你的目标是将这个矩阵变成全 \(0\) 矩阵。如果无法实现,输出
IMPOSSIBLE
,否则输出一个矩阵,表示每个元素被取反的次数。可能有多种方案,这时输出字典序最小的那个。\(1 \leq N,M \leq 15\)
题解
以下“取反”会写成“翻转”。
先说结论:第 \(i+1\) 行的翻转方案,取决于第 \(i\) 行的翻转方案。
证明:显然。
于是我们可以枚举第一行翻转哪些元素(这个我们可以用二进制枚举子集的方法,这样子的优点是自然满足字典序从小到大遍历)。然后遍历矩阵,如果上一行同一列的元素是 \(1\),那么就翻转这个元素。
然后发现前 \(M-1\) 行都会是 \(0\)。只需要看看最后的一行是不是全是 \(0\) 即可。
时间复杂度 \(O(2^N\cdot NM)\)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[20][20];
int d[20];
int reving[20][20],nowret[20][20],ans[20][20],result;
vector<pair<int,int> > deltas = {{1,0},{0,1},{-1,0},{0,-1}};
void doit(int i,int j){
nowret[i][j]++;
reving[i][j]=reving[i][j]==1?0:1;
for(auto delta : deltas){
int x=i+delta.first,y=j+delta.second;
if(x<1||x>n||y<1||y>m) continue;
reving[x][y]=reving[x][y]==1?0:1;
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
result = 1e9;
for(int I=0;I<(1<<m);I++){
memset(nowret,0,sizeof(nowret));
memcpy(reving,a,sizeof(a));
bool flag=1;
for(int j=0;j<m;j++){
if((I>>j)&1) doit(1,j+1);
}
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
if(reving[i-1][j]) doit(i,j);
}
}
for(int i=1;i<=m&&flag;i++){
if(reving[n][i]) flag=0;
}
if(!flag) continue;
int ss = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ss+=nowret[i][j];
}
}
if(ss<result){
result=ss;
memcpy(ans,nowret,sizeof(ans));
}
}
if(result >= (n*m+1)){
cout<<"IMPOSSIBLE";
}
else{
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<ans[i][j]<<' ';
}
cout<<'\n';
}
}
return 0;
}
D. P2329 [SCOI2005]栅栏
题面
农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要 \(n\) 特定规格的木材。于是农夫约翰到木材店购买木材。可是木材店老板说他这里只剩下 \(m\) 个大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为 \(10\) 的木板可以切成长度为 \(8\) 和 \(2\) 的两个木板。
你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰最多能够得到多少他所需要的木板。
\(1 \leq m \leq 50,1 \leq n \leq 10^3\)
题解
首先如果要拿到更多的木材,一定要从小的开始拿,于是可以对需求排序。
其次如果可以拿多必定可以拿少,所以答案具备可二分性。于是二分数量,判定函数考虑搜索,但暴力搜索一定会爆炸,我们考虑剪枝:
- 预处理前缀和,如果剩余木材不够需要,就可行性剪枝。
- 需求排序后重复的在一起,我们可以考虑如果和前一个一样就没必要再计算了。
这样就可以排在最优解第 \(8\) 位了。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[55],b[1005],sum[1005],asum,mid;
int nouse;
bool dfs(int want,int pos){
if(sum[mid]+nouse>asum) return 0;
if(want==0) return 1;
bool f=0;
for(int i=pos;i<=n;i++){
if(a[i]>=b[want]){
a[i]-=b[want];
if(a[i]<b[1]) nouse+=a[i];
if(b[want-1] == b[want]){
f=dfs(want-1,i);
}
else f=dfs(want-1,1);
if(a[i]<b[1]) nouse-=a[i];
a[i]+=b[want];
if(f) return 1;
}
}
return false;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
asum+=a[i];
}
cin>>m;
for(int i=1;i<=m;i++) cin>>b[i];
sort(b+1,b+m+1);
for(int i=1;i<=m;i++) sum[i]=sum[i-1]+b[i];
while(sum[m]>asum&&m) m--;
int l=0,r=m;
while(l<=r){
mid=(l+r)>>1;
if(dfs(mid,1)) l=mid+1;
else r=mid-1;
}
cout<<l-1;
}
E. P1514 [NOIP2010 提高组] 引水入城
题面
给出一个 \(N\times M\) 的矩阵 \(h\)。每个矩阵元素可以是蓄水厂或输水管。蓄水厂的坐标必须为 \((1,)\)。输水管可以是任意的,但同一个元素不能同时作为蓄水厂或输水管。但可以既不是蓄水厂也不是输水管。
水从蓄水厂出发,若 \(h_{a,b}\lt h_{c,d}\) 则水可以从 \((c,d)\) 流到 \((a,b)\)。
你需要求出 位于 \((N,)\) 的所有节点是否有水流过。如果有输出 \(1\),并输出最少需要多少个蓄水厂。如果没有输出 \(0\),并输出最少有多少个位于 \((N,)\) 的节点没有水流过。
\(1 \leq N,M \leq 500,1 \leq h_{i,j} \leq 10^6\)
题解
首先每一个蓄水厂覆盖的一定是底层的一个区间。于是我们可以使用记忆化搜索搞出来每一个节点 \((i,j)\) 如果作为蓄水厂,所覆盖的底层区间 \([l(i,j),r(i,j)]\)。递推公式如下:
&l(i,j)=\begin{cases}
&j&(i=n)\\
&\min\limits_{(i,j) \operatorname{Can\ go\ to}(a,b)} l(a,b)
\end{cases}\\
&r(i,j)=\begin{cases}
&j&(i=n)\\
&\max\limits_{(i,j) \operatorname{Can\ go\ to}(a,b)} r(a,b)
\end{cases}
\end{aligned}
\]
然后遍历 \((N,)\) 看看是否都遍历到了,如果有没有遍历到的那么就是无解。输出没有遍历到的数量。
否则就是一个线段完美覆盖问题,贪心解决即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int h[505][505],l[505][505],r[505][505],vis[505][505];
vector<pair<int,int> > deltas = {{1,0},{0,1},{-1,0},{0,-1}};
void dp(int i,int j){
if(vis[i][j]) return;
vis[i][j]=1;
for(pair<int,int> delta : deltas){
int ni=i+delta.first,nj=j+delta.second;
if((ni<1||ni>n||nj<1||nj>m)||(!(h[i][j]>h[ni][nj]))){
continue;
}
dp(ni,nj);
l[i][j]=min(l[i][j],l[ni][nj]);
r[i][j]=max(r[i][j],r[ni][nj]);
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m;
memset(l,0x7f,sizeof(l));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>h[i][j];
if(i==n){
l[i][j]=j;r[i][j]=j;
}
}
}
for(int i=1;i<=m;i++){
if(!vis[1][i]) dp(1,i);
}
int nowater = 0;
for(int i=1;i<=m;i++){
if(!vis[n][i]) nowater++;
}
if(nowater) cout<<0<<'\n'<<nowater;
else{
cout<<1<<'\n';
int cursor=1;
int tot=0;
while(cursor<=m){
int ans=0;
for(int i=1;i<=m;i++){
if(l[1][i]<=cursor){
ans=max(ans,r[1][i]);
}
}
cursor=ans+1;
tot++;
}
cout<<tot;
}
return 0;
}
F. CF888E Maximum Subsequence
题面
给出一个长度为 \(n\) 的序列 \(a\),输出在模 \(M\) 意义下最大的子序列和。
\(1 \leq n \leq 35,1 \leq M,a_i \leq 10^9\)
题解
- 我会骗分!输出 \(M-1\) 可以获得 \(75\) 分的好成绩。
- 我会折半搜索(Meet in Middle)!可以获得 \(100\) 分。
我们考虑先将序列 \(a\) 的所有元素模 \(M\)。然后对半暴力搜索。然后考虑如何合并答案。
先对左边(左右随意)的所有答案 \(L\) 排序,然后枚举右边的所有答案 \(R\)。假设枚举到 \(R_i\)。先用 lower_bound
二分出不大于 \(M-1-R_i\) 的值的位置 \(p\)。如果这个值正好等于我们要二分的数。那么直接输出 \(M-1\)。否则带给这个数的最大和一定是 \(L_{p-1}\) 或 \(\max L\)。(因为 \(0\leq L_i,R_i\lt M\),所以 \(\max L\) 是 \(\geq p\) 中最好的,\(p-1\) 是 \(\lt p\) 中最好的)。
时间复杂度 \(O(n\cdot 2^{n\div2})\)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,ansl,ansr;
int a[1000005],lans[1000005],rans[1000005],lansc,ransc;
int m;
void dfsl(int dep,int sum){
if(dep>(n>>1)){
lans[++lansc]=sum;
return;
}
dfsl(dep+1,sum);
dfsl(dep+1,(sum+a[dep])%m);
}
void dfsr(int dep,int sum){
if(dep>n){
rans[++ransc]=sum;
return;
}
dfsr(dep+1,sum);
dfsr(dep+1,(sum+a[dep])%m);
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]%=m;
}
if(n&1) n++;
dfsl(1,0);
dfsr((n>>1)+1,0);
sort(lans+1,lans+lansc+1);
int ans=*max_element(a+1,a+n+1);
for(int i=1;i<=ransc;i++){
int v=rans[i];
int x = lower_bound(lans+1,lans+lansc+1,m-1-v)-lans;
if(x==lansc+1){
ans=max(ans, (v+lans[lansc])%m);
}
else if(lans[x] == (m-1-v)){
cout<<(m-1)<<'\n';
return 0;
}
else{
int leftnear=0,maxv=lans[lansc];
if(x!=1) leftnear = lans[x-1];
ans=max(ans,max((v+leftnear)%m, (v+maxv)%m));
}
}
cout<<ans<<'\n';
return 0;
}
/*
now=7 m=15
10 11 12 13 14
*/
G. P3230 [HNOI2013]比赛
题面
有 \(N\) 支球队,每支球队按照 \(3-1-0\) 积分制进行比赛。已知每个球队的最后得分,输出有多少个不同的比赛方案。答案可能很大,你只需要对 \(10^9+7\) 取模即可。
\(3 \leq N \leq 100\),保证数据有解。
题解
代码
test20230109考试总结-2023寒搜索专题的更多相关文章
- HDU(搜索专题) 1000 N皇后问题(深度优先搜索DFS)解题报告
前几天一直在忙一些事情,所以一直没来得及开始这个搜索专题的训练,今天做了下这个专题的第一题,皇后问题在我没有开始接受Axie的算法低强度训练前,就早有耳闻了,但一直不知道是什么类型的题目,今天一看,原 ...
- NOIP2018提高组金牌训练营——搜索专题
NOIP2018提高组金牌训练营——搜索专题 1416 两点 福克斯在玩一款手机解迷游戏,这个游戏叫做”两点”.基础级别的时候是在一个n×m单元上玩的.像这样: 每一个单元有包含一个有色点.我们将用不 ...
- 搜索专题:Balloons
搜索专题:Balloons 这道题一看与时间有关,第一想到的就是BFS,定义一个状态,包含每一个状态的剩余气球数,已经进行的时间和每一个志愿者上一次吹气球的时间: 每一次状态转换时,检查是否有没有使用 ...
- 2014 UESTC暑前集训搜索专题解题报告
A.解救小Q BFS.每次到达一个状态时看是否是在传送阵的一点上,是则传送到另一点即可. 代码: #include <iostream> #include <cstdio> # ...
- 【PHP高效搜索专题(2)】sphinx&coreseek在PHP程序中的应用实例
PHP可以通过三种途径来调用sphinx 通过Sphinx官方提供的API接口(接口有Python,Java,Php三种版本) 通过安装SphinxSE,然后创建一个中介sphinxSE类型的表,再通 ...
- 【PHP高效搜索专题(1)】sphinx&Coreseek的介绍与安装
我们已经知道mysql中带有"%keyword%"条件的sql是不走索引的,而不走索引的sql在大数据量+大并发量的时候,不仅效率极慢还很有可能让数据库崩溃.那我们如何通过某些关键 ...
- 2015 UESTC 搜索专题F题 Eight Puzzle 爆搜
Eight Puzzle Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/61 ...
- 2015 UESTC 搜索专题B题 邱老师降临小行星 记忆化搜索
邱老师降临小行星 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/61 Des ...
- 蓝桥杯dfs搜索专题
2018激光样式 #include<bits/stdc++.h> using namespace std; /* dfs(i) 第i个激光机器 有两种选择:vis[i-1] == 0 时 ...
- 搜索专题:问题 E: 挑战ACM迷宫
这是往年校赛的一道题,最开始做这道题的时候还没有系统的学习过搜索,用了C语言学的回溯法尝试,毫无疑问的TLE: 学习了DFS之后,自己的剪枝功力不够,又是TLE,最后学了BFS之后,哇,终于做出来了, ...
随机推荐
- 记录一次sshd服务启动失败
记录一次sshd服务启动失败 问题描述: 服务器开机之后发现无法通过远程连接服务器终端,但是服务器并未宕机,于是考虑到sshd服务出现异常 解决思路: 查看服务器sshd服务运行情况 [root@ha ...
- 手把手教你从安装CentOS7.4镜像开始,搭建IoT视频监控系统
摘要:在CentOS7.4服务器版本的环境下安装nginx服务器.配置文件服务器.流媒体服务器. 本文分享自华为云社区<华为云ECS服务器安装CentOS7.4镜像,部署GINX服务器.搭建物联 ...
- Java开发学习(三十九)----SpringBoot整合mybatis
一.回顾Spring整合Mybatis Spring 整合 Mybatis 需要定义很多配置类 SpringConfig 配置类 导入 JdbcConfig 配置类 导入 MybatisConfig ...
- iOS App 上架App Store及提交审核详细教程
上架App Store审核分7步进行: 1.安装iOS上架辅助软件Appuploader 2.申请iOS发布证书(p12) 3.申请iOS发布描述文件(mobileprovision) 4.打包ipa ...
- 基于FPGA的AES加解密IP
Programmable AES Encryption/ Decryption IP 可编程AES加解密IP 可编程AES加解密IP提供了加解密算法功能,兼容美国国家标准与技术研究院(NIST)发布的 ...
- 【k8s连载系列】2. k8s整体架构
# 一.Kubernetes的整体架构 学习k8s,最终目的是为了部署应用,部署一个完整的k8s, 就要知道k8s的组成.k8s主要包含两大部分: 中间包含三个绿色包的是master服务器. 下面是n ...
- Java学习之Filter与Listener
0x00前言 web中的Filiter:当客户端访问服务端资源的时候,过率器可以把请求拦截下来,完成一些特殊的功能 完成的操作一般都是通用的作用:列如登录验证. web中的Listener一般用于加载 ...
- 顺序表代码总结——SqList
在C++编译器下可直接运行 #include <stdio.h> #include <stdlib.h> #include <malloc.h> //顺序表存储结构 ...
- 学习ASP.NET Core Blazor编程系列十三——路由(完)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- Sql Server日期转汉字字符串
以下脚本转至互联网,增加了自己需要的功能并改成了函数的方式 SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ================== ...