8.18考试总结(NOIP模拟43)[第一题·第二题·第三题·第四题]
愿你和重要的人,在来日重逢。
前言
题目名字起的很随意。。。
这天 Luogu 的运势好像是大凶(忌:打模拟赛,注意报零)。
但是考得还不错,拿到了这么多场模拟赛以来第二三个场上AC。
所以说,我爱大凶
T1 第一题
解题思路
官方题解好像不干人事,直接咕了。。
其实做法都差不多,都是乱搞(反正我是这么干的)。
对于整棵树上的每一个点开一个 multiset,用来维护子树内剩余的每一个士兵的深度。
然后每一次进行子树合并的时候选择深度最小的,但是到当前子树根节点距离的两倍小于根节点深度的点进行利用。
可以这么理解:把整个过程视作最后停留的节点的深度加上其它经过节点到 该节点与停留节点的LCA的距离的二倍(毕竟要下去再上来嘛)
然后就再开一个数组记录上面有来回的时间的贡献。
时间复杂度我不会算,好像是 n 的,但是我的 set 合并的时候都没有启发式合并,感觉最坏的情况会被搞成接近 \(n^2\)。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10,M=1e3+10;
int n,ans,fa[N],f[N],dep[N],mx[N];
vector<int> v[N];
multiset<int> s[N];
void add_edge(int x,int y)
{
v[x].push_back(y);
}
void dfs(int x)
{
mx[x]=dep[x];
for(int i=0;i<v[x].size();i++)
{
int to=v[x][i];
if(to==fa[x]) continue;
fa[to]=x;
dep[to]=dep[x]+1;
dfs(to);
mx[x]=max(mx[x],mx[to]);
}
}
void merge(multiset<int> &a,multiset<int> &b)
{
for(auto it=b.begin();it!=b.end();it++)
a.insert((*it)+1);
}
bool comp(int x,int y)
{
return mx[x]<mx[y];
}
void solve(int x)
{
bool flag=false;
sort(v[x].begin(),v[x].end(),comp);
for(int i=0;i<v[x].size();i++)
{
int to=v[x][i];
if(to==fa[x]) continue;
flag=true; solve(to);
for(auto it=s[x].begin();it!=s[x].end();it++)
{
if((*it)>=dep[x]||!s[to].size()) break;
auto it2=(--s[to].end());
if((*it2)+1<(*it)) break;
f[x]+=(*it)*2;
s[to].erase(it2);
s[x].erase(it);
s[x].insert((*it2)+1);
}
f[x]+=f[to];
merge(s[x],s[to]);
}
if(!flag) s[x].insert(0);
}
signed main()
{
n=read();
for(int i=1,x,y;i<n;i++)
{
x=read(); y=read();
add_edge(x,y);
add_edge(y,x);
}
dfs(1);
solve(1);
for(auto it=s[1].begin();it!=s[1].end();it++)
ans+=(*it);
printf("%lld",ans+f[1]);
return 0;
}
T2 第二题
解题思路
做法依旧非常暴力。(但是好像别的做法也可以被极端数据卡掉,比如n=1,m=1e5)
一眼看出是二分答案。
二分最小的差值,判断是否可以用 \(\le k\) 的步数实现。
用一种类似于 SPFA 的方法实现判断。
为了保证每一个点都被扫到,先把所有的点都放到队列里面。
然后扫描每一个点周围的点的最大值,看看差值是否符合条件,不符合的话就更改并计入贡献。
接下来再次扫描周围的六个点,对于不合法的更改计入贡献,对于更改过但是不再队列里的入队。
最后把贡献和与 k 比较就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e3+10,M=1e5+10,INF=1e18;
int n,m,K,minn=INF,maxn;
int d1[10]={0,0,0,1,-1,1,-1};
int d2[10]={0,1,-1,0,0,-1,1};
bool vis[M];
vector<int> s[M],p[M];
pair<int,int> id;
struct Queue
{
int l,r;
pair<int,int> num[M<<2];
bool empty(){return l>r;}
pair<int,int> front(){return num[l];}
void clear(){l=1;r=0;}
void push(pair<int,int> temp){num[++r]=temp;}
void pop(){l++;};
}q;
int pos(int x,int y)
{
return (x-1)*m+y;
}
bool check(int mid)
{
memset(vis,true,sizeof(vis));
int sum=0; q.clear();
for(int i=1;i<=n;i++)
{
p[i]=s[i];
for(int j=1;j<=m;j++)
q.push(make_pair(i,j));
}
while(!q.empty())
{
int x=q.front().first,y=q.front().second;
q.pop(); vis[pos(x,y)]=false;
int maxn=0;
for(int i=1;i<=6;i++)
{
int x2=x+d1[i],y2=y+d2[i];
if(x2<=0||y2<=0||x2>n||y2>m) continue;
maxn=max(maxn,p[x2][y2]);
}
if(maxn-p[x][y]>mid)
{
sum+=maxn-p[x][y]-mid;
p[x][y]=maxn-mid;
}
if(sum>K) return false;
for(int i=1;i<=6;i++)
{
int x2=x+d1[i],y2=y+d2[i];
if(x2<=0||y2<=0||x2>n||y2>m) continue;
if(abs(p[x][y]-p[x2][y2])>mid)
{
sum+=p[x][y]-p[x2][y2]-mid;
p[x2][y2]=p[x][y]-mid;
if(!vis[pos(x2,y2)]) q.push(make_pair(x2,y2)),vis[pos(x2,y2)]=true;
}
}
if(sum>K) return false;
}
return sum<=K;
}
signed main()
{
n=read(); m=read(); K=read();
for(int i=1;i<=n;i++) s[i].push_back(0);
for(int i=1;i<=n;i++)
for(int j=1,x;j<=m;j++)
{
x=read();
s[i].push_back(x);
minn=min(minn,s[i][j]);
if(maxn<=s[i][j]) id=make_pair(i,j);
maxn=max(maxn,s[i][j]);
}
int l=0,r=maxn-minn,ans=maxn-minn;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)){r=mid-1;ans=mid;}
else l=mid+1;
}
printf("%lld",ans);
return 0;
}
T3 第三题
解题思路
看出来是数位 DP 了,奈何我太弱,不会。。
\(f_{i,j,l,r}\) 表示当先考虑到第 i 位,已经填上了 j 个 1,是否压紧上下界。
储存两个值:当前状态的数字个数,以及所有数字的总和。
为了方便我们计算 a 到 INF 以及 b 到 INF 的值,最后减一下就好了。
先记忆化DFS一遍算出限制内合法的个数。
然后就类似于查询排名,根据第一维的数字个数求出合法的数字总和。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int Lg=30;
struct Node
{
int x,y;
}f[Lg+10][Lg+10][2][2];
int T,li,ri,a,b;
Node dfs_unlim(int i,int j,bool l,bool r)
{
if(i<0||j<0) return (Node){0ll,0ll};
if(~f[i][j][l][r].x) return f[i][j][l][r];
int lim1=l&(li>>i),lim2=r&(!((ri>>i)&1));
Node ans1=(Node){0ll,0ll},ans2=(Node){0ll,0ll};
if(!lim1) ans1=dfs_unlim(i-1,j,(!((li>>i)&1))&l,(!((ri>>i)&1))&r);
if(!lim2) ans2=dfs_unlim(i-1,j-1,(li>>i)&l,(ri>>i)&r);
return f[i][j][l][r]=(Node){ans1.x+ans2.x,ans1.y+ans2.y+ans2.x*(1<<i)};
}
Node dfs_lim(int i,int j,bool l,bool r,int rk)
{
if(i<0||j<0||!rk) return (Node){0ll,0ll};
if(~f[i][j][l][r].x&&f[i][j][l][r].x<=rk) return f[i][j][l][r];
int lim1=l&(li>>i),lim2=r&(!((ri>>i)&1));
Node ans1=(Node){0ll,0ll},ans2=(Node){0ll,0ll};
if(!lim1) ans1=dfs_lim(i-1,j,(!((li>>i)&1))&l,(!((ri>>i)&1))&r,rk);
if(!lim2) ans2=dfs_lim(i-1,j-1,(li>>i)&l,(ri>>i)&r,rk-ans1.x);
return (Node){ans1.x+ans2.x,ans1.y+ans2.y+ans2.x*(1<<i)};
}
int work(int rk)
{
int sum=0;
for(int i=0;i<=Lg;i++)
if(rk>=f[Lg][i][1][1].x)
{
if(!(~f[Lg][i][1][1].x)) continue;
rk-=f[Lg][i][1][1].x;
sum+=f[Lg][i][1][1].y;
}
else
{
sum+=dfs_lim(Lg,i,1,1,rk).y;
break;
}
return sum;
}
int solve()
{
li=read(); ri=read(); b=read(); a=read();
a=ri-li+1-a+1; b=ri-li+1-b+1;
memset(f,-1,sizeof(f));
f[0][0][0][0]=f[0][0][0][1]=(Node){1ll,0ll};
f[0][0][1][0]=f[0][0][1][1]=(Node){!(li&1),0ll};
f[0][1][0][0]=f[0][1][1][0]=(Node){1ll,1ll};
f[0][1][0][1]=f[0][1][1][1]=(Node){ri&1,ri&1};
for(int i=1;i<=Lg;i++)
f[Lg][i][1][1]=dfs_unlim(Lg,i,1,1);
return work(b)-work(a-1);
}
signed main()
{
T=read();
while(T--) printf("%lld\n",solve());
return 0;
}
T4 第四题
解题思路
题面描述的不太清楚,我说一下自己的理解。
对于一个序列,保证每一位都是在 \([1,n]\) 范围内的,并且前 i 位的最大值等于 前 i-1 位的最大值,或者只比它多 1 。(这个序列并不一定是 \(1\sim n\) 的排列)
\(f_{i,j}\) 表示选择 i 个数最大的数字为 j 的方案数。
\(g_{i,j}\) 表示在最大值为 j 的序列后再选择 i 个数的方案数。
这两个方程可以互补求出整个序列的值。
因为\(k^2=C_{k}^{2}\times 2 + k\),所以每一种方案的贡献是 1+后面再选一个 x 的方案数 \(\times 2\)
可以的得出在第 i 个位置的 x 的贡献就是:
\]
\]
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e3+10;
int n,mod,f[N][N],g[N][N],s[N][N],ans[N];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
signed main()
{
n=read(); mod=read();
f[0][0]=f[1][1]=1;
for(int i=1;i<=n;i++)
g[0][i]=1;
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
add(f[i][j],(f[i-1][j]*j%mod+f[i-1][j-1])%mod);
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i;j++)
add(g[i][j],(g[i-1][j]*j%mod+g[i-1][j+1])%mod);
for(int i=1;i<=n;i++)
for(int j=i;j>=1;j--)
add(s[i][j],(f[i-1][j]*(g[n-i][j]+2*(n-i)*g[n-i-1][j]%mod)%mod+s[i][j+1])%mod);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
add(ans[j],(s[i][j]+f[i-1][j-1]*(g[n-i][j]+2*(n-i)*g[n-i-1][j]%mod))%mod);
for(int i=1;i<=n;i++)
printf("%lld ",(ans[i]+mod)%mod);
return 0;
}
改题记录

8.18考试总结(NOIP模拟43)[第一题·第二题·第三题·第四题]的更多相关文章
- 8.18考试总结[NOIP模拟43]
又挂了$80$ 好气哦,但要保持优雅.(草 T1 地衣体 小小的贪心:每次肯定从深度较小的点向深度较大的点转移更优. 模拟一下,把边按链接点的子树最大深度排序,发现实际上只有上一个遍历到的点是对当前考 ...
- [考试总结]noip模拟43
这个题目出的还是很偷懒.... 第一题...第二题...第三题...四.... 好吧... 这几次考得都有些问题,似乎可能是有些疲惫,脑袋也是转不太动,考完总觉得自己是能力的问题,但是改一分钟之后会发 ...
- 团队作业4——第一次项目冲刺(Alpha版本)第一天+第二天+第三天+第四天+第五天+第六天+第七天
冲刺第一天 一.Daily Scrum Meeting照片 二.每个人的工作 1.今天计划完成的任务 GUI.计时功能.题目生成 2.工作中遇到的困难 刚开始在计时功能模块只能做到秒位,经过查询资料后 ...
- 2021.10.18考试总结[NOIP模拟76]
T1 洛希极限 不难发现每个点肯定是被它上一行或上一列的点转移.可以预处理出每个点上一行,上一列最远的能转移到它的点,然后单调队列优化. 预处理稍显ex.可以用并查集维护一个链表,记录当前点之后第一个 ...
- 2021.9.18考试总结[NOIP模拟56]
T1 爆零 贪心地想,肯定要先走完整个子树再走下一个,且要尽量晚地走深度大的叶子.所以对每个点的儿子以子树树高为关键字排序$DFS$即可. 也可$DP$. $code:$ T1 #include< ...
- 5.23考试总结(NOIP模拟2)
5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...
- 5.22考试总结(NOIP模拟1)
5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...
- NOI.AC NOIP模拟赛 第一场 补记
NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...
- 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]
6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...
- 编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三
编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三 编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三
随机推荐
- Docker安装使用--Centos
目录 前言 Docker安装使用 安装步骤 卸载旧版本 安装utils 配置utils的存储库 安装Docker 安装指定版本Docker 验证安装 官网安装说明 阿里云镜像加速 Docker使用 帮 ...
- el-row el-col 的点击事件@click 没反应
el-col 是vue封装的组件,不支持原生事件的触发.要想触发事件需要加修饰符".native" 无效果: <el-col :span="4" @cli ...
- 力扣539(java)-最小时间差(中等)
题目: 给定一个 24 小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示. 示例 1: 输入:timePoints = [" ...
- 即学即会 Serverless | 初识 Serverless
简介:Serverless 架构被越来越多的业务所采纳,成为其技术选型,大多数开发者已经跨越对 Serverless 概念了解,切实向落地实践出发.本文带大家一探究竟,为什么说 Serverless ...
- 基于 EventBridge 构建 SaaS 应用集成方案
简介:事件源是事件驱动的基石,如何获取更多事件源也是 EventBridge 一直在探索和尝试的方向.针对市场上其他云厂商和垂直领域的 Saas 服务,EventBridge 发布了 HTTP So ...
- 阿里云消息队列 RocketMQ 5.0 全新升级:消息、事件、流融合处理平台
简介: RocketMQ5.0 的发布标志着阿里云消息从消息领域正式迈向了"消息.事件.流"场景大融合的新局面.未来阿里云消息产品的演进也将继续围绕消息.事件.流核心场景而开展. ...
- ACMMM2021|在多模态训练中融入“知识+图谱”:方法及电商应用实践
简介: 随着人工智能技术的不断发展,知识图谱作为人工智能领域的知识支柱,以其强大的知识表示和推理能力受到学术界和产业界的广泛关注.近年来,知识图谱在语义搜索.问答.知识管理等领域得到了广泛的应用. ...
- DataWorks功能实践速览 05——循环与遍历
简介: DataWorks功能实践系列,帮助您解析业务实现过程中的痛点,提高业务功能使用效率!通过往期的介绍,您已经了解到在DataWorks上进行任务运行的最关键的几个知识点,其中上期参数透传中为 ...
- 在线运行代码的 PHP 沙盒环境实现
演示: 多版本PHP运行代码 作用: 方便作为独立的调试环境运行 一些 临时逻辑,查看执行结果. 方便比较不同版本的 PHP 执行差异,进行一般的兼容性测试. 思路: Docker镜像构建多个PH ...
- [FE] uni-app 安装 uview-ui 的两种方式
一. 下载的方式安装 就是把源码放到项目根目录中,然后引入 scss.js,并配置 easycom 模式. https://www.uviewui.com/components/install.htm ...