NOIP模拟测试「简单的区间·简单的玄学·简单的填数·简单的序列」
简单的区间
$update$
终于$AC$了
找到$(sum[r]+sum[l](sum表示以中间点为基准的sum)-mx)\%k==0$的点
注意这里$sum$表示是以$mid$为基准点,(即$sum[l]$为后缀和,$sum[r]$为前缀和)
回忆$(sum[r]-sum[l])\%k==0$这个经典问题做法(入阵曲简化版),开桶,桶里维护$sum[l]\%k$,那么$r$贡献就是桶里$sum[r]\%k$个数
于是这个题开桶维护$sum$,问题转化为求$max$即可
记录$max$位置是否$>mid$,区别对待
设$f[i][0]$表示$max$在$mid$右面,$f[i][1]$表示$max$在$mid$左面
$f[i][0]$存下右面$sum[r]-mx$,找桶里是否存在左面$sum[l]$
$f[i][1]$存下右面$sum[r]$ 找到左面是否存在$mx-sum[l]$
完了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 1010101
ll f[A][2],a[A],pos[A],mx[A],sum[A];
ll cnt,n,k,ans;
void solve(ll l,ll r){
if(l==r) return ;
ll mid=(l+r)>>1;
cnt=sum[mid]=mx[0]=0;
for(ll i=mid+1;i<=r;i++){
if(a[i]>a[mx[cnt]]) mx[++cnt]=i;
sum[i]=(sum[i-1]+a[i])%k;
f[(sum[i]-a[mx[cnt]]%k+k)%k][0]++;
pos[i]=mx[cnt];
// printf("f[%lld]=%lld sum=%lld cnt=%lld\n",(sum[i]-a[mx[cnt]]%k+k)%k,f[(sum[i]-a[mx[cnt]]%k+k)%k][0],sum[i],cnt);
}
mx[cnt+1]=r+1;
ll suml=0,rnow=mid+1,mxl=0,p=1;
for(ll i=mid;i>=l;i--){
suml=(suml+a[i])%k;
mxl=max(mxl,a[i]);
while(p<=cnt&&a[mx[p]]<=mxl) p++;
while(rnow<mx[p]) {
f[(sum[rnow]-a[pos[rnow]]%k+k)%k][0]--;
f[sum[rnow]%k][1]++;
rnow++;
}
// printf("ans=%lld f[%lld][1]=%lld p=%lld rnow=%lld mx[%lld]=%lldsum[%lld]=%lld\n",ans,(k+mxl%k-suml)%k,f[(k+mxl%k-suml)%k][1],p,rnow,p,mx[p],rnow,sum[rnow]);
ans+=f[(mxl-suml+k)%k][1];
if(p<=cnt) ans+=f[(k-suml)%k][0];
// printf("ans=%lld f[%lld][0]=%lld\n",ans,k-suml,f[(k-suml)%k][0]);
}
for(ll i=mid+1;i<rnow;i++)
f[sum[i]][1]--;
for(ll i=rnow;i<=r;i++)
f[(sum[i]-a[pos[i]]%k+k)%k][0]--;
solve(l,mid);solve(mid+1,r);
}
int main(){
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
solve(1,n);
printf("%lld",ans);
}
简单的玄学
题解
题目中说至少两个相同那么答案就所有方案-全不相同
所有方案${(2^n)}^m=2^{n*m}$,
互不相同,首先第一个随便选剩下避开已经选过就行$2^n*2^{n-1}......2^{n-m+1}$
那么题目很傻逼的让你取模并且约分,你需要先约分再取模(取模再约分的话这个题就太水了,所以是先约分再取模)
思考怎么约分
下面全是$2$多少次方,于是我们看上面多少个二就行了
$2^n*(2^{n}-1)......({2^n}-m+1)$很恶心,思考转化
性质:$2^n-x$中二个数$=$$x$中二的个数
证明:假设$x$可以表示为$j*(2^w)$(j可以是分数),乘法分配率$2^w*(2^{n-w}-j)$后面这个里面没有别的$2$因子了,原式$=$$2^w$,又$j$中没有$2$因子故相乘因子数不变,得证
那么原式就变成求$(m-1)!$里$2$因子数
可以简单求
for(ll i=1;(1ll<<i)<=m-1;i++){
(ercnt+=(m-1)/(1ll<<i));
}
例如$1$ $2$ $3$ $4$ $5$ $6$ $7$ $8$这个序列
分别有$(2^1)*2$,$(2^2)*1$,$(2^3)*1$那么就是$8/8+8/4+8/2$
可以看作$/2$时给所有有$2$因子填上一个二(即$2$,$4$,$6$,$8$中填一个2),此时$4$还剩$1$个没填$8$还剩$2$个没填
$/4$给$4$,$8$里填此时$8$还剩$1$个没填
最后$/8$,全部填满
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e6+3;
const ll phi=1e6+2;
ll x,y,n,m;
ll meng(ll x,ll k){
ll ans=1;
for(;k;k>>=1,x=x*x%mod)
if(k&1)
ans=ans*x%mod;
return ans;
}
ll gcd(ll x,ll y){
if(y==0) return x;
return gcd(y,x%y);
}
int main(){
// freopen("sd.txt","w",stdout);
scanf("%lld%lld",&n,&m);
/* if(log(m)>n){
printf("1 1\n");
return 0;
}
*/ ll maxn=meng(2,n%phi);
y=meng(maxn,m%phi);
x=1;
ll ercnt=n;
for(ll i=0;i<m;i++){
// printf("maxn-i=%lld i=%lld m=%lld x=%lld\n",maxn-i,i,m,x);
x=x*(maxn-i)%mod;
if(!x) break;
}//2逆元500002
for(ll i=1;(1ll<<i)<=m-1;i++){
(ercnt+=(m-1)/(1ll<<i));
printf("ercnt=%lld 1<<=%lld\n",ercnt-n,1ll<<i);
}
y=y*meng(500002,ercnt)%mod;
x=x*meng(500002,ercnt)%mod;
printf("%lld %lld\n",(y-x+mod)%mod,y);
}
简单的填数
题解
一个$up$代表填的上界,$down$代表填的下界
先不考虑已经填了的
$up$两位一进,$down$五位一进
考虑已经填的
先考虑上界
若$a[i]>up$比上界大肯定不合法
若$a[i]=up$取$min(2,up)$
若$a[i]<up$则将$up$调整到$a[i]$次数变为$2$
下界类似
若$a[i]<down$比下界小不合法
若$a[i]>down$将$down$调整到$a[i]$
统计答案时反着扫
序列为什么不是$up$呢
7
0 0 0 2 0 2 0
正解
2
1 1 2 2 2 2 2
用up:
2
1 1 2 2 3 2 2
代码
/*
7
0 0 0 2 0 2 0
hack
2
1 1 2 2 2 2 2
up:
2
1 1 2 2 3 2 2
10f
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 1010101
struct node {
ll cnt,x;
}up[A],down[A];
ll n;
ll a[A],tong[A];
int main(){
// freopen("da.in","r",stdin); freopen("ans.bf","w",stdout);
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
if(a[1]!=1&&a[1]!=0){
printf("-1\n");
return 0;
}
up[1].cnt=1,up[1].x=1;
down[1].cnt=1,down[1].x=1;
for(ll i=2;i<=n;i++){
up[i]=up[i-1],down[i]=down[i-1];
if(++up[i].cnt>2) up[i].cnt=1,up[i].x++;
if(++down[i].cnt>5) down[i].cnt=1,down[i].x++;
if(a[i]){
if(up[i].x>a[i]){
up[i].x=a[i];
up[i].cnt=2;
}
else if(up[i].x==a[i]){
up[i].cnt=min(up[i].cnt,2ll);
}
if(down[i].x<a[i])
down[i].x=a[i],down[i].cnt=1;
if(up[i].x<a[i]||down[i].x>a[i]){
printf("-1\n");
return 0;
}
}
}
if(up[n].cnt==1){
up[n].x=up[n-1].x;
}
if(up[n].x<down[n].x){
printf("-1\n");
return 0;
}
printf("%lld\n",up[n].x);
tong[up[n].x]=1;
a[n]=up[n].x;
for(ll i=n-1;i>=1;i--){
if(!a[i]){
ll t=min(a[i+1],up[i].x);
if(tong[t]==5) t--;
a[i]=t;
}
tong[a[i]]++;
}
for(ll i=1;i<=n;i++){
printf("%lld ",a[i]);
}
}
简单的序列
这是一个简单$dp$,但我觉得很棒在此写下题解
真的非常简单,
有一个长度$n$括号序列(只有$"()"$ ),给定其中长度为$m$一段,求满足括号匹配方案数
$n,m<=1e6$ $n-m<=4000$
题解
性质:我们发现一个合法匹配序列左扩号时刻比右括号多(显然),最后左扩号数量等于右括号数量
设$f[i][j]$表示长度为$i$序列,左扩号比右括号多$j$个方案数
那么类似的设$g[i][j]$为右括号比左扩号多$j$的方案数
(其实$f$和$g$值完全一样)
转移非常简单
当前括号可能是$($则贡献$f[i][j]=f[i-1][j-1]$为$)$则$f[i][j]=f[i-1][j+1]$
总贡献$f[i][j]=f[i-1][j-1]+f[i-1][j+1]$
类似的$g[i][j]=g[i-1][j-1]+g[i-1][j+1]$
那么思考统计答案
其实也非常简单
枚举第一段长度$i$,第一段左扩号比右括号多$j$,设给定序列左扩号比右括号多$j$
$ans=\sum\limits_{i=1}^{i<=n-m} \sum\limits_{j=0}^{j<=i} f[i][j]*g[(n-m)-i][j+tot]$
注意判是否合法
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 4040
const ll mod=1e9+7;
char c[2020202];
ll f[A][A];
ll tot,mint,n,m,ans;
int main(){
// freopen("da.in","r",stdin); freopen("ans.bf","w",stdout);
scanf("%lld%lld",&n,&m);
scanf("%s",c+1);
for(ll i=1;i<=m;i++){
if(c[i]=='(')
tot++;
else tot--;
if(i==1) mint=tot;
else mint=min(mint,tot);
}
f[0][0]=1;
for(ll i=1;i<=n-m;i++){
for(ll j=0;j<=i;j++){
if(j==0) f[i][j]=f[i-1][j+1];
else f[i][j]=(f[i-1][j+1]+f[i-1][j-1])%mod;
}
}
for(ll i=0;i<=n-m;i++){
for(ll j=0;j<=i;j++){
if(j+mint>=0&&j+tot<=n-m)
ans=(ans+f[i][j]*f[(n-m)-i][j+tot]%mod)%mod;
}
}
printf("%lld\n",ans);
}
我没数据,也没法提交,和$std$对拍了一下
下面是我的数据生成及对拍
1 #include<bits/stdc++.h>
2 using namespace std;
3 int main(){
4 system("g++ bf.cpp -o bf");
5 system("g++ sol.cpp -o sol");
6 system("g++ da.cpp -o da");
7 int rp=0;
8 while(++rp){
9 cout<<rp<<" ";
10 system("./da");
11 system("./sol");
12 system("./bf");
13 if(system("diff -B -b ans.sol ans.bf")){
14 puts("WA");
15 while(1);
16 }
17 puts("AC");
18 }
19 }
对拍
1 #include<bits/stdc++.h>
2 using namespace std;
3 int main(){
4 freopen("da.in","w",stdout);
5 srand(time(NULL));
6
7 int m=rand()%10000+300;
8 int c=rand()%m+1;
9 while(m-c>2000){
10 c=rand()%m+1;
11 }
12 cout<<m<<" "<<c<<endl;
13 for(int i=1;i<=c;i++){
14 if(rand()%2){
15 printf("(");
16 }
17 else printf(")");
18 }
19 cout<<endl;
20 }
数据生成
NOIP模拟测试「简单的区间·简单的玄学·简单的填数·简单的序列」的更多相关文章
- NOIP模拟13「工业题·卡常题·玄学题」
T1:工业题 基本思路 这题有一个重要的小转化: 我们将原来的函数看作一个矩阵,\(f(i,j-1)*a\)相当于从\(j-1\)向右走一步并贡献a,\(f(i-1,j)*b\)相当于从\(i-1 ...
- 「题解」NOIP模拟测试题解乱写II(36)
毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色
2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...
- NOIP模拟测试17&18
NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组
2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)
2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...
- 「题解」NOIP模拟测试题解乱写I(29-31)
NOIP模拟29(B) T1爬山 简单题,赛时找到了$O(1)$查询的规律于是切了. 从倍增LCA那里借鉴了一点东西:先将a.b抬到同一高度,然后再一起往上爬.所用的步数$×2$就是了. 抬升到同一高 ...
- NOIP模拟测试19「count·dinner·chess」
反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍 ...
- NOIP模拟测试1(2017081501)
好,今天是cgg第一次举行模拟测试,希望各位支持. 时间限制:2小时 题目链接: 题目一:水得都没名字了 题目二:车站 题目三:选数 不要觉得2小时太少,我的题目很良心,都很简单. 答案可以在模拟测试 ...
随机推荐
- Codeforces Round #691 (Div. 2)
A. Red-Blue Shuffle 题意:有两个长度为n的数组,数组a和数组b,问那个数组中的数字相比之下比另一个数组中相应位置的元素值更大一些,如果数组a大就输出RED,如果数组b大就输出BLU ...
- 射线与空间内三角形的相交检测算法(Möller-Trumbore)的推导与实践
背景介绍(学习算法之前需要先了解) 射线与空间内三角形的相交检测是游戏程序设计中一个常见的问题,最典型的应用就是拾取(Picking),本文介绍一个最常见的方法,这个方法也是DirectX中采用的方法 ...
- 前端面试 CSS三大特性
CSS的三大特性 1.层叠性 代码由上向下执行,相同选择器设置到同一元素上,样式冲突的,会执行比较靠近html的样式,样式不冲突的情况下不影响 代码如下 <!DOCTYPE html> & ...
- JAVA并发(1)-AQS(亿点细节)
AQS(AbstractQueuedSynchronizer), 可以说的夸张点,并发包中的几乎所有类都是基于AQS的. 一起揭开AQS的面纱 1. 介绍 为依赖 FIFO阻塞队列 的阻塞锁和相关同步 ...
- Vue | 指令实现自动翻译填充英文名功能
背景:应用系统中存在多个创建实体表单,表单填写时,在填写中文名称后,要填写对应的英文名作为标识或数据库查询索引. 需求:填写中文名的同时,系统自动生成英文名并填充到表单中,辅助用户操作,节约操作时间. ...
- ubuntu下载安装软件并创建图标
本列以安装webstorm软件 1.官网下载软件的压缩包 2.解压 umlinux@umlinux-PC:~/idea$ tar -zxvf ideaIU-2020.3.1.tar.gz 3.找到we ...
- [转载]好好好centos7 安装 KVM
centos7 安装 KVM 2019-04-03 09:58:24 34 0 0 一.基础环境准备 centos7.X 最小环境安装 关闭selinux # vi /etc ...
- 深入 Go 语言 defer 实现原理
转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/523 本文使用的go的源码 1.15.7 介绍 defer 执行规 ...
- .NET Worker Service 添加 Serilog 日志记录
前面我们了解了 .NET Worker Service 的入门知识[1] 和 如何优雅退出 Worker Service [2],今天我们接着介绍一下如何为 Worker Service 添加 Ser ...
- MySQL 通过.frm文件和.ibd文件实现InnoDB引擎的数据恢复
起因是这样的,公司的领导表示说服务器崩了,修理好之后,只剩下数据库目录下的物理文件(即.frm文件与.ibd文件).然后,整了一份压缩包给我,叫我瞅一下能不能把数据恢复出来.我当场愣了一下,这都啥文件 ...