冲刺 CSP 联训模拟2
温馨提示
代码中的变量名可能与题意、题解不同。
代码不删缺省源,可以直接拿来对拍。
T1 挤压
Solution
众所周知,异或是一种按位运算,不好进行十进制的数间合并。我们考虑将每个数拆分为二进制的形式进行处理。
此时,对于每一种情况,假设表示 \(2^i\) 二进制位的值为 \(b_i\),我们的答案(平方)的形式应为:
\]
这相当于对于第 \(i\) 位与第 \(j\) 位(\(i\leq{j}\)),对答案产生贡献的应为这两位是否同时为 \(1\)。对于本题,允许实现 \(\log^2\) 级别的算法,所以考虑对于每个数,枚举每两个数位,根据该数这两位的情况确定这两个数位在选择是否异或该数后分别为 \(00\),\(01\),\(10\),\(11\) 的概率(等于期望)并带入上面式子。
Optimise
考虑上面这个实现分讨的量很大,我们考虑将第 \(i\) 和 \(j\) 位是否为 \(1\) 的情况转为 \(2b_i+b_j\),即将 \(00\) 的情况记作 \(0\),\(01\) 的情况记作 \(1\),以此类推。考虑将枚举到的数的这两位的情况记作 \(t\),将已经得到的记作 \(i\) 的概率记为 \(dp_i\),则新的 \(dp_i\) 包含两种情况:一种是根本没有选到,从 \(dp_i\) 转移,另一种是取到该数,从 \(dp_x\) 转移,其中 \(x \operatorname{xor} t=i\)。根据异或的自反律,可得:
\]
其中 \(p\) 表示当前的数被选中的概率。
Code
代码(峰值时间 $793$ ms 峰值内存 $2.8$ Mib 代码长度 $1.2$ Kib)
#include<bits/stdc++.h>
using namespace std;
const long long p=1000000007;
inline long long qpow(long long x,long long y){
long long rtr=1;
for(;y;y>>=1){
if(y&1) rtr=rtr*x%p;
x=x*x%p;
}
return rtr;
}
long long n,m,a[100100],tinv,ps[200100],ne[200100],dp[8][64][64],ans;
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%lld",&n);
tinv=qpow(1000000000,p-2);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=1;i<=n;++i){
scanf("%lld",&ps[i]);
ps[i]=ps[i]*tinv%p;
ne[i]=((1-ps[i])%p+p)%p;
}
for(int i=0;i<30;++i){
for(int j=0;j<30;++j) dp[0][i][j]=1;
}
long long tb,tem[8];
for(int i=1;i<=n;++i){
for(int j=0;j<30;++j){
for(int k=j;k<30;++k){
tem[0]=dp[0][j][k];
tem[1]=dp[1][j][k];
tem[2]=dp[2][j][k];
tem[3]=dp[3][j][k];
tb=(((a[i]>>j)&1)<<1)|((a[i]>>k)&1);
for(int l=0;l<4;++l) dp[l][j][k]=(tem[l]*ne[i]%p+tem[l^tb]*ps[i]%p)%p;
}
}
}
for(int i=0;i<30;++i){
for(int j=0;j<30;++j){
if(i^j) ans=(ans+(((1ll<<(i+j))%p*dp[3][i][j]%p)<<1)%p)%p;
else ans=(ans+(1ll<<(i+j))%p*dp[3][i][j]%p)%p;
}
}
printf("%lld",ans);
return 0;
}
T2 工地难题
Solution
假设不考虑 \(k\),相当于将 \(n-m\) 个 \(0\) 插进 \(m\) 个 \(1\),方案数为 \(\dbinom{n}{n-m}\)。考虑取出 \(1\) 组 \(k+1\) 个点再加进去,也就是说钦定 \(1\) 组大于 \(k\),方案数为 \(\dbinom{n-k-1}{n-m}\dbinom{1}{n-m+1}\),即现将剩下的数插板再将钦定的放入 \(n-m\) 个 \(0\) 之间的空中。然而我们发现,有其中一组在 \(k\) 与 \(2k\) 之间的可以通过在全在剩下的中取或取一部分剩下的再加上钦定的被算重两次,我们再钦定 \(2k+2\) 减去贡献。以此类推进行容斥,最终最大连续段的长度小于等于\(k\) 的值为:
\]
进行差分即可得到每个 \(k\) 的答案。
对于 \(1\leq{k}\leq{m}\),每一个 \(k\) 至多进行 \(O(\frac{n}{k})\) 级别的计算,总的复杂度为调和级数的,即 \(O(n\log{m})\)。
证明
考虑将 \(1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+\cdots+\frac{1}{m}\) 拆成 \(1+\frac{1}{2}+(\frac{1}{3}+\frac{1}{4})+(\frac{1}{5}+\frac{1}{6}+\frac{1}{7}+\frac{1}{8})+\cdots\),之后每一组的元素个数为上一组的两倍,发现每一组的和都小于 \(1\),有对数级别的组数,乘上分子 \(n\) 得到 \(O(n\log{m})\)。
Code
代码(峰值时间 $45$ ms 峰值内存 $7.7$ Mib 代码长度 $934$ B)
#include<bits/stdc++.h>
using namespace std;
const long long p=1000000007;
long long n,m,t[400100],invt[400100],lans;
inline long long qpow(long long x,long long y){
long long rtr=1;
for(;y;y>>=1){
if(y&1) rtr=rtr*x%p;
x=x*x%p;
}
return rtr;
}
inline long long c(long long x,long long y){
if(x>y) return 0;
return t[y]*invt[x]%p*invt[y-x]%p;
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%lld%lld",&n,&m);
t[0]=invt[0]=1;
for(int i=1;i<=(n<<1);++i) t[i]=i*t[i-1]%p;
invt[n<<1]=qpow(t[n<<1],p-2);
for(int i=(n<<1)-1;i;--i) invt[i]=(i+1)*invt[i+1]%p;
for(int i=1;i<=m;++i){
long long ans=c(n-m,n);
for(int j=m-i-1,k=1;j>=0;j-=i+1,++k){
if(k&1) ans=((ans-c(n-m,j+n-m)*c(k,n-m+1)%p)%p+p)%p;
else ans=(ans+c(n-m,j+n-m)*c(k,n-m+1)%p)%p;
}
printf("%lld ",((ans-lans)%p+p)%p);
lans=ans;
}
return 0;
}
T3 星空遗迹
Solution
我们先考虑对“最终胜者”操作进行化简,可以发现以下性质:
- 对于 \(AAAA\) ,只有前一个和后一个影响比较结果,如果后面大了前面所有的 \(A\) 也跟着变,所以 \(AAAA\) 等价于 \(A\)。
- 对于 \(ABA\) ,其中 \(A\) 能赢 \(B\),在第一局必定为 \(AAX\),\(X\) 表不确定。我们发现这个字符串只要最后一个字母是 \(A\) 就不会影响他与后面比较的结果,即 \(X\) 不变,所以 \(ABA\) 等价于只有一个 \(A\)。
至此,我们将某个字母前面是该字母或它能赢的字母的情况进行了化简,得到一种稳定情况:某个字母前面是他赢不了的一个字母,反映到本题,则稳定的串一定为 \(RSPRSPRSPRSP\cdots\) 的一个子串。考虑维护一个单调栈,在栈内维护稳定结构,即当当前字母为 \(A\) 且 \(A\) 赢 \(B\),\(C\) 赢 \(A\) 时:
- 若栈为空,则将 \(A\) 入栈。
- 若栈顶为 \(C\),加入 \(A\) 后依旧稳定,直接入栈。
- 若栈顶为 \(A\),因为栈是稳定结构要么只有 \(A\) 要么 \(A\) 前面有 \(C\),只需弹出原来的 \(A\) 再入栈就稳定了。
- 若栈顶为 \(B\),因为栈是稳定结构要么只有 \(B\) 要么 \(B\) 前面有 \(A\),只有 \(B\) 则清栈加 \(A\),否则按照上文第二个性质去掉 \(AB\) 并将 \(A\) 入栈。
容易发现,由于稳定结构每个字符都无法更新前一个字符,所以加入每个字符后“最终胜者”为此时栈顶字母。
以上操作放到本题复杂度为 \(O(nq)\),考虑将整个字符串入栈,对某个区间查询有以下情况:
- 存在栈的大小为 \(1\),则最后一次出现这种情况的时候进栈的元素即为栈底。
- 不存在这种情况,考虑忽略前面字符,最后一次栈最小时入栈元素要么是第一个进来没被消除,要么就是清空了栈,所以他就是栈底。
现在考虑与上文相对应的,\(A\) 入栈是如何统计栈的大小。设上一次大小为 \(f_{i-1}\),此次为 \(f_i\)。
- 若栈为空,\(f_i=1\)。
- 若栈顶为 \(C\),直接入栈,\(f_i=f_{i-1}+1\)。
- 若栈顶为 \(A\),弹出 \(A\) 再入栈,\(f_i=f_{i-1}\)。
- 若栈顶为 \(B\),弹出 \(AB\) 再入栈,\(f_i=\max(f_{i-1}-1,1)\)。
考虑对于 \(1\) 取 \(\max\) 很麻烦,我们考虑在 \(f_{i-1}\leq{1}\) 时仍然减 \(1\),考虑如果出现连续减 \(1\),说明后加的优于前面的,取后面,也就是 \(f\) 较小的。而且可以发现,如果存在多个最小值,说明当前栈顶无法在被更新。问题转化为:求区间内任意一个 \(f\) 最小的点的字符,即线段树区间求最小值位置。
对于修改,我们考虑每次修改 \(i\) 位置只会影响 \(f_i-f_{i-1}\) 和 \(f_{i+1}-f{i}\),这样会影响后面所有的 \(f\),记录每个 \(f_i-f_{i-1}\) 并据此区间修改 \(f\) 即可。
Code
代码(峰值时间 $180$ ms 峰值内存 $8.3$ Mib 代码长度 $2.5$ Kib)
#include<bits/stdc++.h>
using namespace std;
struct node{
int data[800100],pos[800100],tag[800100];
inline void pushdown(int now){
if(tag[now]){
data[now<<1]+=tag[now];
tag[now<<1]+=tag[now];
data[now<<1|1]+=tag[now];
tag[now<<1|1]+=tag[now];
tag[now]=0;
}
}
void build(int now,int lft,int rgt,int* dt){
if(lft==rgt){
data[now]=dt[lft];
pos[now]=lft;
return;
}
pushdown(now);
int mid=(lft+rgt)>>1;
build(now<<1,lft,mid,dt);
build(now<<1|1,mid+1,rgt,dt);
if(data[now<<1]<data[now<<1|1]){
data[now]=data[now<<1];
pos[now]=pos[now<<1];
}else{
data[now]=data[now<<1|1];
pos[now]=pos[now<<1|1];
}
}
void add(int now,int lft,int rgt,int ll,int rr,int dt){
if(ll<=lft&&rgt<=rr){
data[now]+=dt;
tag[now]+=dt;
return;
}
pushdown(now);
int mid=(lft+rgt)>>1;
if(ll<=mid) add(now<<1,lft,mid,ll,rr,dt);
if(rr>mid) add(now<<1|1,mid+1,rgt,ll,rr,dt);
if(data[now<<1]<data[now<<1|1]){
data[now]=data[now<<1];
pos[now]=pos[now<<1];
}else{
data[now]=data[now<<1|1];
pos[now]=pos[now<<1|1];
}
}
pair<int,int> query(int now,int lft,int rgt,int ll,int rr){
if(ll<=lft&&rgt<=rr) return {data[now],pos[now]};
pushdown(now);
bool vis=0;
int mid=(lft+rgt)>>1;
pair<int,int> rtr,tem;
if(ll<=mid) rtr=query(now<<1,lft,mid,ll,rr),vis=1;
if(rr>mid){
tem=query(now<<1|1,mid+1,rgt,ll,rr);
if(vis&&rtr.first>tem.first) rtr=tem;
else rtr=tem;
}
return rtr;
}
}tree;
char a[200100];
int n,q,f[200100],s[200100];
inline int getv(char x){
if(x=='R') return 2;
if(x=='S') return 1;
return 0;
}
inline int cmp(char x,char y){
if(x==y) return 0;
x=getv(x),y=getv(y);
if((!x)&&y==2) return 1;
if(x==2&&(!y)) return -1;
if(x>y) return 1;
return -1;
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d %s ",&n,&q,a+1);
f[1]=s[1]=1;
for(int i=2;i<=n;++i){
f[i]=cmp(a[i-1],a[i]);
s[i]=f[i]+s[i-1];
}
tree.build(1,1,n,s);
char tc;
int ta,tb,td,tem;
for(;q;--q){
scanf("%d",&ta);
if(ta==1){
scanf("%d %c ",&tb,&tc);
a[tb]=tc;
if(tb^1){
tem=cmp(a[tb-1],a[tb]);
tree.add(1,1,n,tb,n,tem-f[tb]);
f[tb]=tem;
}
if(tb^n){
tem=cmp(a[tb],a[tb+1]);
tree.add(1,1,n,tb+1,n,tem-f[tb+1]);
f[tb+1]=tem;
}
}else{
scanf("%d%d",&tb,&td);
printf("%c\n",a[tree.query(1,1,n,tb,td).second]);
}
}
return 0;
}
T4 纽带
待更。
冲刺 CSP 联训模拟2的更多相关文章
- 2014-11-1 NOIP模拟赛1
冲刺NOIP2014复赛模拟题第六套第二试 题目名称 日历游戏 最大公约数 密码 英文代号 calendar gcd pasuwado 输入文件名 calendar.in gcd.in pasuw ...
- 【CJOJ P1957】【NOIP2010冲刺十模拟赛】数字积木
[NOIP2010冲刺十模拟赛]数字积木 Description 小明有一款新式积木,每个积木上都有一个数,一天小明突发奇想,要是把所有的积木排成一排,所形成的数目最大是多少呢? 你的任务就是读入n个 ...
- csp模拟赛低级错误及反思
\(csp\)模拟赛低级错误及反思. 1.没开\(longlong\). 反思:注意数据类型以及数据范围. 2.数组越界(前向星数组未开两倍,一题的数据范围应用到另一题上,要开两倍的写法为开两倍数组) ...
- 冲刺NOIP2015提高组复赛模拟试题(五)2.道路修建
2.道路修建 描述 Description liouzhou_101最悲痛的回忆就是NOI2011的道路修建,当时开了系统堆栈,结果无限RE… 出于某种报复心理,就把那题神奇了一下: 在 Z星球上有N ...
- NOIP2017金秋冲刺训练营杯联赛模拟大奖赛第一轮Day2题解
上星期打的...题有点水,好多人都AK了 T1排个序贪心就好了 #include<iostream> #include<cstring> #include<cstdlib ...
- CSP 201612-3 权限查询 【模拟+STL】
201612-3 试题名称: 权限查询 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 授权 (authorization) 是各类业务系统不可缺少的组成部分,系统用户通过授权 ...
- CSP复赛day2模拟题
没错,我又爆零了.....先让我自闭一分钟.....so 当你忘记努力的时候,现实会用一记响亮的耳光告诉你东西南北在哪. 好了,现在重归正题: 全国信息学奥林匹克联赛(NOIP2014) 复赛模拟题 ...
- 【CSP模拟赛】Freda的迷宫(桥)
题目描述 Freda是一个迷宫爱好者,她利用业余时间建造了许多迷宫.每个迷宫都是由若干房间和走廊构成的,每条走廊都连接着两个不同的房间,两个房间之间最多只有一条走廊直接相连,走廊都是双向通过. 黄昏 ...
- 冲刺Noip2017模拟赛8 解题报告——五十岚芒果酱
1.鼎纹 [问题描述] 据说鼎纹的 种制造 式是 铜模印出来的,这是我国古代劳动 智慧 的结晶.铜模印过的地 ,会留下深深的印记,经过时间的炼化,洗 练成历史的遗存. 聪明的古代劳动人民拥有一个 a ...
- 冲刺Noip2017模拟赛7 解题报告——五十岚芒果酱
1.二叉树(binary) .二叉树 (binary.cpp/c/pas) [问题描述] 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: ()若左子树不空,则左子树上所有结点的值均小于它的根结 ...
随机推荐
- 【Java】修饰符
修饰符(Modifier):是用于限定类型以及类型成员的声明的一种符号. 其用来定义类.方法或者变量,通常放在语句的最前端. 例子: public class Person { default Str ...
- ZKmall开源商城iOS 与安卓双端开发:如何平衡 B2B2C 商城的代码复用与性能
在ZKmall开源商城的iOS与安卓双端开发中,平衡B2B2C商城的代码复用与性能是一个关键考量.以下是一些建议和实践方法,以实现这一目标: 一.架构分层设计:解耦与复用 1. 分层架构模型 merm ...
- C#/.NET/.NET Core技术前沿周刊 | 第 32 期(2025年3.24-3.31)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- Unity Shader模板测试-描边
Unity Shader模板测试描边效果,常用于 rpg 项目中主角被遮挡的情况,将被遮挡的部分的轮廓描边绘制出来,这样可以在任何情况都能知道主角在哪里.(还有另外一种就是使用X光效果,但这种效果不需 ...
- Cocos Creator3.x小白常见问题笔记&官方视频教程合集收藏分享
小白常见问题 为什么会有这篇笔记? 这篇笔记旨在答疑解惑官方文档或视频教程里忽略掉的细节.对于小白来说这些细节没人提醒或浪费很多时间,但在熟悉的人眼里这都是些什么问题,回都懒得回. (别问我怎么知道的 ...
- C#——基于CancellationTokenSource实现Task的取消
参照:第七节:利用CancellationTokenSource实现任务取消和利用CancellationToken类检测取消异常. - Yaopengfei - 博客园 (cnblogs.com) ...
- maven setting.xml文件配置
官网文档:http://maven.apache.org/ref/3.2.5/maven-settings/settings.html 下面是我个人的配置 <?xml version=" ...
- C# 14 新增功能一览,你觉得实用吗?
前言 今天咱们一起来看看在 C# 14 中新增的几个功能特性,是否给我们日常编码带了来便利. 前提准备 要体验 C# 14 中的新增功能,你需要安装最新的 Visual Studio 2022 版本或 ...
- Axure RP医疗在线挂号问诊原型图医院APP原形模板
Axure RP医疗在线挂号问诊原型图医院APP原形模板 医疗在线挂号问诊Axure RP原型图医院APP原形模板,是一款原创的医疗类APP,设计尺寸采用iPhone13(375*812px),原型图 ...
- Java 原生异步编程与Spring 异步编程 详解
简介 Java 异步编程是现代高性能应用开发的核心技术之一,它允许程序在执行耗时操作(如网络请求.文件 IO)时不必阻塞主线程,从而提高系统吞吐量和响应性. 异步 vs 同步 同步:任务按顺序执行,后 ...