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第三
随机推荐
- 重新点亮linux 命令树————二进制安装[十一八]
前言 简单介绍一下二进制安装 正文 wget https://openresty.org/download/openresty-1.15.8.1.tar.gz tar -zxf openresty-V ...
- 重新整理.net core 计1400篇[三] (.net core 如何源代码调试和查看源码 )
前言 本来这里是写源代码的,因为vs没有那么容易调试查看到源代码,所以先把调试源代码和查看源代码弄完. 正文 需要修改一些vs配置,这个vs也是有要求的,要vs2017. 那么你还需要加载远程符号. ...
- Jenkins集成GitLab的正确姿势,实现Git代码提交触发CI/CD
❝ jenkins和gitlab是目前DevOps工具链中最常见的,抛开gitlab-ci不谈,gitlab代码提交触发jenkins流水线是最经典的搭配. 这里就介绍下如何配置实现jenkins和g ...
- Pygame安装以及解决问题:Try to run this command from the system terminal. Make sure that you use the correct version of 'pip......
在这里记录一下我的安装过程: 1.首先找到自己python程序安装目录下的Scripts文件夹(里面有pip这里面): 2.使用快捷键win + R 打开终端,先进入到安装python的盘符,然后进入 ...
- 多年锤炼,迈向Kata 3.0 !走进开箱即用的安全容器体验之旅| 龙蜥技术
简介: 袋鼠与 Kata 将会碰撞出什么样的火花? 文/云原生 SIG(Special Interest Group) 一.Kata 的过去 让我们将时钟拨回 2015 年 5 月,Hyper.sh ...
- 基于 OPLG 从 0 到 1 构建统一可观测平台实践
简介: 随着软件复杂度的不断提升,单体应用架构逐步向分布式和微服务的架构演进,整体的调用环境也越来越复杂,仅靠日志和指标渐渐难以快速定位复杂环境下的问题.对于全栈可观测的诉求也变得愈加强烈,Trace ...
- Dubbo-go 服务代理模型
简介:HSF 是阿里集团 RPC/服务治理 领域的标杆,Go 语言又因为其高并发,云原生的特性,拥有广阔的发展前景和实践场景,服务代理模型只是一种落地场景,除此之外,还有更多的应用场景值得我们在研发 ...
- WPF 列表控件数据源绑定多个数据集合方法
在 WPF 用的多的列表控件如 ListBox 或 ListView 等,本文告诉大家在这些列表控件上进行绑定多个数据集合来源的多个实现方法.如有一个显示动物列表的控件,需要绑定的数据来源是阿猫和阿狗 ...
- k8s问题解决
问题1: 问题描述:k8s中Terminating状态pod不能删除 [root@master ~]# kubectl get pods -n ms NAME READY STATUS RESTART ...
- DE10-Lite加速度计使用教程
DE10-Lite加速度计使用教程 1. 概述 DE10-Lite实验板上有一片5轴加速度计芯片ADXL345(通常称为G-sensor).它可以用来测量板子的倾斜角度.本文讲述如何以50次/秒的速度 ...