6.20考试总结(NOIP模拟9)[斐波那契·数颜色·分组]
一旦你尝试过天空的味道,你就会永远向上仰望
T1 斐波那契
解题思路
\(70pts\)做法
这个做法比较暴力,考场上也是看到范围\(10^{12}\)后知道需要推式子,但是感觉自己太菜了,没敢轻易尝试,然后就去搞\(10^6\)的部分分去了。。。
比较好理解,直接暴力建树(其实就是找到每个节点的父亲节点),然后直接在树上搞 LCA 就好了,代码实现上也没有什么太大问题。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10,M=N<<1;
int n,m,cnt=1,las=1,f[N][25],dep[N];
struct Ques
{
int a,b;
}q[N];
inline void build()
{
while(cnt<n)
{
int temp=las;
las=cnt;
for(int i=1;i<=temp;i++)
{
f[++cnt][0]=i;
dep[cnt]=dep[i]+1;
if(cnt>=n)
break;
}
}
}
inline void LCA_init()
{
f[1][0]=1;
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int LCA_ask(int x,int y)
{
if(x==y)
return x;
if(dep[x]>dep[y])
swap(x,y);
for(int i=20;i>=0;i--)
if(dep[x]<=dep[f[y][i]])
y=f[y][i];
if(x==y)
return x;
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int main()
{
// freopen("fibonacci2.in","r",stdin);
// freopen("fibonacci2.out","w",stdout);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].a,&q[i].b);
n=max(n,max(q[i].a,q[i].b));
}
build();
LCA_init();
/*
for(int i=1;i<=n;i++)
{
cout<<i<<"To: ";
for(int j=head[i];j;j=nxt[j])
cout<<ver[j]<<' ';
cout<<endl;
}
*/
for(int i=1;i<=m;i++)
printf("%d\n",LCA_ask(q[i].a,q[i].b));
return 0;
}
\(100pts\) 做法
仔细观察序列,可以发现第i个月出生的第j只兔子的编号是\(f(i-1)+j\)父亲就是j,显然\(j<f(i-1)\),然后移一下项:
\]
然后我们再斐波那契数列上二分(用lower_bound实现)。
对于每一个询问,选择编号大的向上跳父亲节点,直到找到LCA为止(记得开long long)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4,M=3e5+50;
int n,m,cnt=2,f[N];
struct Ques
{
int a,b;
}q[M];
#undef int
int main()
{
// #define int register long long
#define int long long
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&q[i].a,&q[i].b);
n=max(n,max(q[i].a,q[i].b));
}
f[1]=f[2]=1;
for(int i=3;i<=N;i++)
{
f[i]=f[i-1]+f[i-2];
if(f[i]>=n)
{
cnt=i;
break;
}
}
for(int i=1;i<=m;i++)
{
int a=q[i].a,b=q[i].b;
while(a!=b)
{
if(a<b)
a^=b^=a^=b;
int num=lower_bound(f+1,f+cnt+1,a)-f-1;
int temp=max(1ll,num);
a-=f[temp];
}
printf("%lld\n",a);
}
return 0;
}
T2 数颜色
解题思路
本题做法有很多,也有许多非常好的方法,诸如线段树,主席树,二分之类的;
这里主要说一下来自wtz大佬的分块做法:
很简单,对于每一个块内的不同颜色分别计算个数,更改的时候直接更改个数,以及原序列就好了。
需要注意的就是每个块的大小 \(\dfrac{1}{2}\)次方会MLE ,\(\dfrac{2}{3}\) 次方会TLE,因此我们有了。。。
\]
于是我们愉快地干掉了此题。当然似乎别的次方也可以,可以自行尝试。。。
code
线段树
主席树
二分
分块
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,len,tot,pos[N],s[N],sum[N][115],li[N],ri[N];
inline int ask(int l,int r,int col)
{
int p=pos[l],q=pos[r],ans=0;
if(p==q)
{
for(int i=l;i<=r;i++)
ans+=(s[i]==col);
return ans;
}
for(int i=p+1;i<=q-1;i++)
ans+=sum[col][i];
for(int i=l;i<=ri[p];i++)
ans+=(s[i]==col);
for(int i=li[q];i<=r;i++)
ans+=(s[i]==col);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
len=pow(n,0.625);
tot=n/len;
// cout<<tot<<endl;
for(int i=1;i<=n;i++)
scanf("%d",&s[i]);
for(int i=1;i<=tot;i++)
{
li[i]=(i-1)*len+1;
ri[i]=i*len;
}
if(ri[tot]<n)
{
tot++;
li[tot]=ri[tot-1]+1;
ri[tot]=n;
}
for(int i=1;i<=tot;i++)
for(int j=li[i];j<=ri[i];j++)
{
pos[j]=i;
sum[s[j]][i]++;
}
for(int i=1,l,r,col,x,opt;i<=m;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d%d",&l,&r,&col);
printf("%d\n",ask(l,r,col));
}
else
{
scanf("%d",&x);
if(pos[x]!=pos[x+1])
{
sum[s[x]][pos[x]]--;
sum[s[x+1]][pos[x+1]]--;
sum[s[x]][pos[x+1]]++;
sum[s[x+1]][pos[x]]++;
}
swap(s[x],s[x+1]);
}
}
return 0;
}
T3 分组
解题思路
参考了题解区一篇思路非常好的题解,在这里讲一下自己的见解。
首先明确一下 K 的取值只有 1 或者 2 这里看数据范围非常重要!,对于 \(K=1\),\(K=2\) 的情况要分开来做。
K=1
对于 \(K=1\) 的情况,为了保证字典序最小,我们需要倒着枚举序列了。
然后再次观察数据范围,发现\(131072 \times2=512^2\),因此我们可以枚举 \(1 \sim 512\) ,令 vis[i] 表示在当前扫到的组里颜色为 i 的是否存在,查看是否访问过 \(x^2-s_i\) 。
如果访问过,表示和第 i 只兔子发生矛盾的已经在这个组里了,因此需要再次分一个组,并且记录下分组的边界,清空 vis 数组。
如果没有访问过,把该种颜色的标记成 true 记录就好了。
K=2
几乎同样的思路,我们仍然需要倒着枚举序列。
对于同一组的兔子,状态之可能有两种:同一小团体或者在敌对小团体,因此我们用并查集维护。
\(\operatorname{find}(1 \sim n)\) 表示 \(1\sim n\)的兔子所在的小团体。
\(\operatorname{find}(n+1 \sim 2 \times n)\) 表示 \(1\sim n\)的兔子所在的小团体的敌对小团体。
然后开一个 vector 数组记录同一颜色的序号,然后分别对于发生矛盾的兔子进行判断,同时更新该兔子所在组以及小团体和敌对小团体。
同样的,如果矛盾无法避免,那就重新开一个组,并清空标记,记录分割点就好了。
- 注意:并查集合并时要在 find 的基础上更新
code
#include<bits/stdc++.h>
using namespace std;
const int M=131080;
int n,m,K,las,s[M],fa[M<<1];
vector<int> ans,v[M<<1];
bool vis[M];
int find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void work_1()
{
for(int i=n;i>=1;i--)
{
bool flag=true;
for(int j=1;j<=512;j++)
if(j*j>=s[i])
if(vis[j*j-s[i]])
{
flag=false;
break;
}
if(!flag)
{
for(int j=i+1;j<las;j++)
vis[s[j]]=false;
ans.push_back(i);
las=i+1;
}
vis[s[i]]=true;
}
printf("%d\n",ans.size()+1);
for(int i=ans.size()-1;i>=0;i--)
printf("%d ",ans[i]);
}
int update(int l,int r)
{
for(int i=l+1;i<r;i++)
vector <int>().swap(v[s[i]]);
ans.push_back(l);
return l+1;
}
void work_2()
{
for(int i=1;i<=(n<<1);i++)
fa[i]=i;
for(int i=n;i>=1;i--)
{
for(int j=1;j<=512;j++)
if(j*j>=s[i])
if(v[j*j-s[i]].size())
for(int k=0;k<v[j*j-s[i]].size();k++)
{
int temp=v[j*j-s[i]][k];
if(find(temp)==find(i))
{
las=update(i,las);
break;
}
else
{
fa[find(i+n)]=find(temp);
fa[find(temp+n)]=find(i);
}
}
v[s[i]].push_back(i);
}
printf("%d\n",ans.size()+1);
for(int i=ans.size()-1;i>=0;i--)
printf("%d ",ans[i]);
}
int main()
{
scanf("%d%d",&n,&K);
las=n+1;
for(int i=1;i<=n;i++)
scanf("%d",&s[i]);
if(K==1)
work_1();
else
work_2();
return 0;
}
6.20考试总结(NOIP模拟9)[斐波那契·数颜色·分组]的更多相关文章
- noip模拟9[斐波那契·数颜色·分组](洛谷模拟测试)
这次考试还是挺好的 毕竟第一题被我给A了,也怪这题太简单,规律一眼就看出来了,但是除了第一题,剩下的我只有30pts,还是菜 第二题不知道为啥我就直接干到树套树了,线段树套上一个权值线段树,然后我发现 ...
- NOIP模拟题 斐波那契数列
题目大意 给定长度为$n$序列$A$,将它划分成尽可能少的若干部分,使得任意部分内两两之和均不为斐波那契数列中的某一项. 题解 不难发现$2\times 10^9$之内的斐波那契数不超过$50$个 先 ...
- noip模拟赛 斐波那契
分析:暴力分有90,真良心啊. a,b这么大,连图都建不出来,肯定是有一个规律.把每个点的父节点写出来:0 1 1 12 123 12345 12345678,可以发现每一个循环的长度刚好是斐波那契数 ...
- NOIP 模拟 9 斐波那契
题解 这是一道推规律的题. 首先,这道题送分不少,先考虑 \(70pts\),直接暴力 \(\mathcal O(n)\) 建边,\(\mathcal O(logn)\) 求 \(lca\) 其次对于 ...
- HDU4549 M斐波那契数
M斐波那契数列 题目分析: M斐波那契数列F[n]是一种整数数列,它的定义例如以下: F[0] = a F[1] = b F[n] = F[n-1] * F[n-2] ( n > 1 ) 如今给 ...
- HDU 5914 Triangle(打表——斐波那契数的应用)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5914 Problem Description Mr. Frog has n sticks, whos ...
- hdu1568&&hdu3117 求斐波那契数前四位和后四位
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1568 题意:如标题所示,求斐波那契数前四位,不足四位直接输出答案 斐波那契数列通式: 当n<=2 ...
- hdu1316(大数的斐波那契数)
题目信息:求两个大数之间的斐波那契数的个数(C++/JAVA) pid=1316">http://acm.hdu.edu.cn/showproblem.php? pid=1316 这里 ...
- leetcode 509. 斐波那契数
问题描述 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) ...
- UVA 11582 Colossal Fibonacci Numbers! 大斐波那契数
大致题意:输入两个非负整数a,b和正整数n.计算f(a^b)%n.其中f[0]=f[1]=1, f[i+2]=f[i+1]+f[i]. 即计算大斐波那契数再取模. 一开始看到大斐波那契数,就想到了矩阵 ...
随机推荐
- Sparse稀疏检索介绍与实践
Sparse稀疏检索介绍 在处理大规模文本数据时,我们经常会遇到一些挑战,比如如何有效地表示和检索文档,当前主要有两个主要方法,传统的文本BM25检索,以及将文档映射到向量空间的向量检索. BM25效 ...
- 力扣392(java)-判断子序列(简单)
题目: 给定字符串 s 和 t ,判断 s 是否为 t 的子序列. 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串.(例如,"ace&quo ...
- Flutter+FaaS一体化任务编排的思考与设计
作者:闲鱼技术-古风 Flutter+Serverless三端一体研发架构,客户端不仅仅是编写双端的代码,而是扩展了客户端的工作边界,形成完整的业务闭环.在新的研发模式落地与实践的过程中,一直在思考如 ...
- 企业版Spark Databricks + 企业版Kafka Confluent 联合高效挖掘数据价值
简介:本文介绍了如何使用阿里云的Confluent Cloud和Databricks构建数据流和LakeHouse,并介绍了如何使用Databricks提供的能力来挖掘数据价值,使用Spark ML ...
- mPaas 研发流程和线上运维介绍
简介: mPaas 研发流程和线上运维介绍 一. 背景 金融级移动开发平台 mPaaS[1](Mobile PaaS)为 App 开发.测试.运营及运维提供云到端的一站式解决方案,能有效降低技术门槛. ...
- 融合趋势下基于 Flink Kylin Hudi 湖仓一体的大数据生态体系
简介: 本文由 T3 出行大数据平台负责人杨华和资深大数据平台开发工程师王祥虎介绍 Flink.Kylin 和 Hudi 湖仓一体的大数据生态体系以及在 T3 的相关应用场景. 本文由 T3 出行大数 ...
- OpenYurt 联手 eKuiper,解决 IoT 场景下边缘流数据处理难题
简介: 云计算的出现促使物联网实现爆炸式增长.在设备规模和业务复杂度不断攀升的趋势之下,边缘计算因其能够将计算能力更靠近网络边缘和设备,从而带来云性能成本的降低,也在这波浪潮之下得到快速发展. 作者 ...
- dotnet 通过 DockerfileContext 解决项目放在里层文件夹导致 VisualStudio 构建失败
本文告诉大家,如何解决 csproj 项目文件放入到里层的文件夹,不放在 sln 所在文件夹的第一层子文件夹,导致 VisualStudio 2022 在构建 docker 映像提示找不到文件的问题 ...
- JS代码优化小技巧
下面介绍一种JS代码优化的一个小技巧,通过动态加载引入js外部文件来提高网页加载速度 [基本优化] 将所有需要的<script>标签都放在</body>之前,确保脚本执行之前完 ...
- 揭露 FileSystem 引起的线上 JVM 内存溢出问题
作者:来自 vivo 互联网大数据团队-Ye Jidong 本文主要介绍了由FileSystem类引起的一次线上内存泄漏导致内存溢出的问题分析解决全过程. 内存泄漏定义(memory leak):一个 ...