即使想放弃,也没法放弃最想要的东西,这就是人

前言

这次应该是和 SDFZ 一起打的第一场比赛吧。

然而我还是 FW 一个。。。

这次考试也有不少遗憾,主要的问题是码力不足,不敢去直面正解,思考程度不够。

改题也比较费劲,记录:

T1 简单的区间

背景

这个题和上一场比赛的第三题一样令人感到亲切,还是在第一时间内想到了单调栈求左右边界。

然后就想到了昨天考试题的恶心程度,想都没想,码完一个单调栈优化的暴力之后就走了。

得分:30pts(\(code\));

解题思路

这个题的做法其实有三种,一种是递归运算的正解,另一种是码量极大的主席树(可持久化权值线段树记录个数)。

这里分别给出官方题解和 zxb 的\(code\);

然后就是来自于 pyt 的 vetor 做法了。

第一步也是运用单调栈求出左右区间,再次不做过多赘述。

接下来就是类似于入阵曲的做法了,求出前缀和(存到 q 数组里)后对于 \(\bmod\;k\) 意义下的不同的余数的数量。

然后就是运用启发式合并的思想进行处理了。

对于暴力左区间的情况,假设当前扫到了 i 合法节点为 j 最大值的下标为 pos 。

满足 \(q_j\) 与 \(q_i+s_{pos}\) 在 \(\bmod\;k\) 意义下相同的序列就是 k 的倍数。

因为,我们只需要在右区间内的符合条件的点的个数。

所以,直接在 vector 里二分查找出右端点和 pos 在 vector 中的下标,相减就是个数了。

同样的 对于暴力右区间的算法也是如此,有一些不同。

  • 注意: 在存储余数为 0 的 vector 数组里要先放进去一个 0 ,因为在暴力右区间的时候会有左端点-1,可能会在序列里没有这个数,并且与入阵曲相似,前零位和的余数也为 0 ,
  • 另外,还有注意一下前缀和的区间端点问题。

code

#include<bits/stdc++.h>
#define int long long
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=5e6+10,M=5e6+10;
int n,mod,ans,top,s[N],sta[N],l[N],r[N],q[N];
vector<int> v[M];
inline void Sta_Init()
{
sta[++top]=1;
l[1]=1;
for(int i=2;i<=n;i++)
{
while(top&&s[sta[top]]<=s[i])
{
r[sta[top]]=i-1;
top--;
}
l[i]=sta[top]+1;
sta[++top]=i;
}
while(top) r[sta[top--]]=n;
}
inline int workl(int li,int ri,int pos)
{
int sum=0;
for(int i=li;i<=pos;i++)
{
int res=(q[i-1]+s[pos]%mod+mod)%mod;
int ls=lower_bound(v[res].begin(),v[res].end(),pos)-v[res].begin();
int rs=upper_bound(v[res].begin(),v[res].end(),ri)-v[res].begin();
sum+=max(rs-ls,0ll);
}
return sum;
}
inline int workr(int li,int ri,int pos)
{
int sum=0;
for(int i=pos;i<=ri;i++)
{
int res=(q[i]-s[pos]%mod+mod)%mod;
int ls=lower_bound(v[res].begin(),v[res].end(),li-1)-v[res].begin();
int rs=upper_bound(v[res].begin(),v[res].end(),pos-1)-v[res].begin();
sum+=max(rs-ls,0ll);
}
return sum;
}
#undef int
int main()
{
#define int register long long
#define ll long long
n=read();
mod=read();
for(int i=1;i<=n;i++)
s[i]=read();
Sta_Init();
for(int i=1;i<=n;i++)
q[i]=(q[i-1]+s[i])%mod;
v[0].push_back(0);
for(int i=1;i<=n;i++)
v[q[i]%mod].push_back(i);
for(int i=1;i<=n;i++)
if(i-l[i]<r[i]-i)
ans+=workl(l[i],r[i],i);
else ans+=workr(l[i],r[i],i);
printf("%lld",ans-n);
return 0;
}

T2 简单的玄学

背景

对于 70pts 的柿子,相信各位都可以很快地搞掉:

\[1-\dfrac{A_{2^n}^{m}}{(2^n)^m}
\]

也就是:

\[\dfrac{(2^n)^m-\prod_{2^n-m+1}^{2^n}}{(2^n)^m}
\]

不难发现对于 \(m<10^6\) 的数据,直接暴力算 \(2^n\) 后的 m 项,然后求一个 \(\gcd\) 就可以了。

但是,取 \(\bmod\) 之后的 \(\gcd\) 就不是原来的 \(\gcd\) 了!

例如:7 和 11 在 \(\bmod\;4\) 的意义下的 \(\gcd\) 是 3 但是他们本身的 \(\gcd\) 是 1 。

于是我们骗到 70pts 的幻想破灭了(可惜考场上的我太傻没有想到这个反例。。)

让我们看一下官方题解:

意会一下之后,我们发现分母的约数只能是 2 的倍数,然后我们就可以求分子上阶乘的 2 的次数。

发现是有一定规律的:每隔 \(2^i\) 就会有一个 \(2^i\) 的倍数,所以就可以 \(\mathcal{O(log_2m)}\) 求出。

但是,更大的该怎么办呢??,不难发现,如果 \(m>\bmod\) 那么分子在 取 \(\bmod\) 之后一定是 0 ,

因此我们只需要求出公约数之后输出两次分母就好了。

code

#include<bits/stdc++.h>
#define int long long
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 mod=1e6+3;
int n,m,base,ans=1;
inline int ksm(int x,int p)
{
int ans=1;
p=p%(mod-1);
while(p)
{
if(p&1) ans=ans*x%mod;
p>>=1;
x=x*x%mod;
}
return ans;
}
#undef int
int main()
{
#define int long long
#define ll long long
n=read();
m=read();
base=ksm(2,n);
int poww=1,sum=0;
while((poww=poww*2)<=m-1)
sum+=(m-1)/poww;
if(m>=mod)
{
int tmp=ksm(ksm(2,sum),mod-2);
base=tmp*ksm(base,m-1)%mod;
printf("%lld %lld",base,base);
return 0;
}
for(int i=1;i<m;i++)
ans=ans*(base-i)%mod;
ans=ans*ksm(ksm(2,sum%(mod-1)),mod-2)%mod;
base=ksm(2,(((m-1)%(mod-1))*(n%(mod-1))+mod-1-sum)%(mod-1))%mod;
printf("%lld %lld",(base-ans+mod)%mod,base%mod);
return 0;
}

T3 简单的填数

背景

这个题思考的不多,码了个暴力就走了,然后小的数据点 TLE 大的直接 WA ,人傻了。

解题思路

先看一下官方题解:

然后我们发现根本看不懂。。。

开两个 pair 数组分别记录贪心到现在位置的最大值以及现在最大值的数量(也就是 up)。

同样的,用 down 记录最小值以及其数量。

对于没有数的位置,直接贪心的向后算就好了( up 类似于二进制, down 类似于五进制)。

对于有数的位置(假设现在要填进去的数为 up 和 down ,现在有的数是 s[i])分为以下几种情况

  1. \(s_i>up\) 或者 \(s_i<down\) 表示这个序列不可能实现,直接输出 -1 。

  2. \(s_i=up\) 或者 \(s_i=down\) 直接该怎样算怎样算就好了。

  3. \(s_i<up\) 对于 up 的值,储存为 \(s_i\),数量记为 2 (因为后面的数要尽量地大)。

  4. \(s_i>down\) 同样的,对于 down 值,也是储存为 \(s_i\) ,但是数量要储存为 1 (因为后面的要尽量的小)。

剩下的就与 down 没有什么关系了,主要运用 up 数组 逆推,特判一下第 n 位,之后尽量对于没有数的挑着大的选就好了,需要用一个数组记录一下每一个数的出现次数。

code

#include<bits/stdc++.h>
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=2e5+10;
int n,ans[N],s[N],vis[N];
pair<int,int> up[N],down[N];
inline void Up_True(int pos,int num)
{
if(num<s[pos])
{
cout<<-1;
exit(0);
}
up[pos].first=s[pos];
up[pos].second=2;
}
inline void Up_False(int pos)
{
if(up[pos-1].second==1)
{
up[pos].first=up[pos-1].first;
up[pos].second=2;
return ;
}
up[pos].first=up[pos-1].first+1;
up[pos].second=1;
}
inline void Get_Up()
{
for(int i=2;i<=n;i++)
{
int temp;
if(up[i-1].second==2) temp=up[i-1].first+1;
else temp=up[i-1].first;
if(s[i]&&s[i]!=temp) Up_True(i,temp);
else Up_False(i);
}
}
inline void Down_True(int pos,int num)
{
if(num>s[pos])
{
cout<<-1;
exit(0);
}
down[pos].first=s[pos];
down[pos].second=1;
}
inline void Down_False(int pos)
{
if(down[pos-1].second<5)
{
down[pos].first=down[pos-1].first;
down[pos].second=down[pos-1].second+1;
return ;
}
down[pos].first=down[pos-1].first+1;
down[pos].second=1;
}
inline void Get_Down()
{
for(int i=2;i<=n;i++)
{
int temp;
if(down[i-1].second==5) temp=down[i-1].first+1;
else temp=down[i-1].first;
if(s[i]&&s[i]!=temp) Down_True(i,temp);
else Down_False(i);
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=read();
up[1].first=up[1].second=down[1].first=down[1].second=1;
Get_Up();
Get_Down();
if(!s[n]) vis[ans[n]=up[n-1].first]++;
else ans[n]=s[n];
for(int i=n-1;i>=1;i--)
{
ans[i]=min(up[i].first,ans[i+1]);
if(vis[ans[i]]>=5) ans[i]--;
vis[ans[i]]++;
}
printf("%d\n",ans[n]);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}

7.12考试总结(NOIP模拟12)[简单的区间·简单的玄学·简单的填数]的更多相关文章

  1. 2021.8.12考试总结[NOIP模拟37]

    T1 数列 考场上切掉的简单题. $a$,$b$与数列中数的正负值对答案无关.全当作正数计算即可. $exgcd$解未知数系数为$a$,$b$,加和为$gcd(a,b)$的不定方程组,再枚举每个数.如 ...

  2. 2021.9.12考试总结[NOIP模拟51]

    T1 茅山道术 仔细观察发现对于每个点只考虑它前面第一个与它颜色相同的点即可. 又仔细观察发现对一段区间染色后以这个区间内点为端点的区间不能染色. 于是对区间右端点而言,区间染色的贡献为遍历到区间左端 ...

  3. [考试总结]noip模拟12

    菜 今天总体来说 菜爆了,打了 \(3\) 个暴力,没有一个是正解,并且每一个分数都低得要命... 主要还是太菜了... 第一题开题发现和昨天 \(T3\) 一样,然而因为还没学可持久化数据结构就咕掉 ...

  4. 2021.10.12考试总结[NOIP模拟75]

    T1 如何优雅的送分 考虑式子的实际意义.\(2^{f_n}\)实际上就是枚举\(n\)质因子的子集.令\(k\)为这个子集中数的乘积,就可以将式子转化为枚举\(k\),计算\(k\)的贡献. 不难得 ...

  5. noip模拟12[简单的区间·简单的玄学·简单的填数]

    noip模拟12 solutions 这次考试靠的还是比较好的,但是还是有不好的地方, 为啥嘞??因为我觉得我排列组合好像白学了诶,文化课都忘记了 正难则反!!!!!!!! 害没关系啦,一共拿到了\( ...

  6. 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]

    6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...

  7. 5.23考试总结(NOIP模拟2)

    5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...

  8. 5.22考试总结(NOIP模拟1)

    5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...

  9. Noip模拟12 2021.7.12

    T1 interval 亏得昨天晚上改掉了T3并且理解了单调栈,今天一扫这题目就知道要用啥了. 先预处理出以a[i]为最大值的最大左右区间.然后再将a[i]取%!!!是的,要不然会影响单调栈的使用.. ...

  10. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

随机推荐

  1. gitlib配置

    第1步:下载Java并安装Java.一般来说下载jdk对应的版本就行. 第2步:配置Java环境变量,具体如下: 1. 新建:变量名:JAVA_HOME:变量值:C:\Program Files\Ja ...

  2. vue使用 elementUI中el-upload的遇到的问题总结

    使用场景,使用el-upload上传文件,选择文件后不立即上传到服务器上,点击提交按钮时与其他form表单数据一起提交,类似的需求,相信有很多小伙伴遇到,可能也会遇到跟我一起的问题,在这里记录一下 & ...

  3. 面试题45(Java)-把数组排成最小的数(中等)

    题目: 输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个. 示例 1: 输入: [10,2] 输出: "102" 示例 2: 输入: [ ...

  4. 自己动手从0开始实现一个分布式RPC框架

    简介: 如果一个程序员能清楚的了解RPC框架所具备的要素,掌握RPC框架中涉及的服务注册发现.负载均衡.序列化协议.RPC通信协议.Socket通信.异步调用.熔断降级等技术,可以全方位的提升基本素质 ...

  5. Apsara Stack 2.0技术百科(9宫格)

    ​ ​ ​​​​​​​​ 原文链接 本文为阿里云原创内容,未经允许不得转载. ​

  6. [FAQ] GitHub 开启二次验证之后,如何通过 https clone 项目 ?

    在 Github Personal Access Tokens 页面,点击生成一个新的 tokon. 此时使用这个 token 作为用户的密码来 clone 项目.

  7. dotnet 教你写一个可以搞炸本机所有 WCF 应用的程序方法

    作为团队里面挖掘机出身的我,怎么能不多挖一些坑好将小伙伴们都埋进去呢.本文来告诉大家一个有趣且简单的方法,此方法可以将本机的 WCF 玩坏,不敢说真的搞炸本机所有 WCF 应用,但搞炸大部分基于 WC ...

  8. 如何参与 .NET 的开发和设计

    现在 dotnet 属于 dotnet 基金会,所有开发者都可以向 dotnet 贡献代码和参与 .NET 的设计,参与路线决策.本文来告诉大家一些基本玩法,带着小伙伴们入坑 注意哦,参与 dotne ...

  9. ChatGPT,我的 .NET 应用该选择哪种日志框架?

    When using .NET 6, compare different main stream third-party logging packages, and give suggestions ...

  10. 第3讲-Cadence分裂元件的制作方法

    笔记3-Cadence分裂元件的制作方法 1.Homogeneous和heterogeneous的区别: 2.创建homogeneous类型元件: 3.创建heterogeneous类型元件. 把元件 ...