题面

温馨提示

代码中的变量名可能与题意、题解不同。

代码不删缺省源,可以直接拿来对拍。

T1 挤压

Solution

众所周知,异或是一种按位运算,不好进行十进制的数间合并。我们考虑将每个数拆分为二进制的形式进行处理。

此时,对于每一种情况,假设表示 \(2^i\) 二进制位的值为 \(b_i\),我们的答案(平方)的形式应为:

\[\left( \sum_{i=0}^{i<30}{2^ib_i}\right)^2=\sum_{i=0}^{i<30}{\sum_{j=i+1}^{j<30}{2^{i+j+1}b_ib_j}}+\sum_{i=0}^{i<30}{2^{2i}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\)。根据异或的自反律,可得:

\[dp_i=dp_i(1-p)+dp_{i \operatorname{xor} t}p
\]

其中 \(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\) 的值为:

\[\sum_{i=0}^{i(k+1)\leq{m}}{(-1)^i \dbinom{n-i(k+1)}{n-m} \dbinom{n-m+1}{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的更多相关文章

  1. 2014-11-1 NOIP模拟赛1

    冲刺NOIP2014复赛模拟题第六套第二试   题目名称 日历游戏 最大公约数 密码 英文代号 calendar gcd pasuwado 输入文件名 calendar.in gcd.in pasuw ...

  2. 【CJOJ P1957】【NOIP2010冲刺十模拟赛】数字积木

    [NOIP2010冲刺十模拟赛]数字积木 Description 小明有一款新式积木,每个积木上都有一个数,一天小明突发奇想,要是把所有的积木排成一排,所形成的数目最大是多少呢? 你的任务就是读入n个 ...

  3. csp模拟赛低级错误及反思

    \(csp\)模拟赛低级错误及反思. 1.没开\(longlong\). 反思:注意数据类型以及数据范围. 2.数组越界(前向星数组未开两倍,一题的数据范围应用到另一题上,要开两倍的写法为开两倍数组) ...

  4. 冲刺NOIP2015提高组复赛模拟试题(五)2.道路修建

    2.道路修建 描述 Description liouzhou_101最悲痛的回忆就是NOI2011的道路修建,当时开了系统堆栈,结果无限RE… 出于某种报复心理,就把那题神奇了一下: 在 Z星球上有N ...

  5. NOIP2017金秋冲刺训练营杯联赛模拟大奖赛第一轮Day2题解

    上星期打的...题有点水,好多人都AK了 T1排个序贪心就好了 #include<iostream> #include<cstring> #include<cstdlib ...

  6. CSP 201612-3 权限查询 【模拟+STL】

    201612-3 试题名称: 权限查询 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 授权 (authorization) 是各类业务系统不可缺少的组成部分,系统用户通过授权 ...

  7. CSP复赛day2模拟题

    没错,我又爆零了.....先让我自闭一分钟.....so 当你忘记努力的时候,现实会用一记响亮的耳光告诉你东西南北在哪. 好了,现在重归正题: 全国信息学奥林匹克联赛(NOIP2014) 复赛模拟题 ...

  8. 【CSP模拟赛】Freda的迷宫(桥)

    题目描述 Freda是一个迷宫爱好者,她利用业余时间建造了许多迷宫.每个迷宫都是由若干房间和走廊构成的,每条走廊都连接着两个不同的房间,两个房间之间最多只有一条走廊直接相连,走廊都是双向通过.  黄昏 ...

  9. 冲刺Noip2017模拟赛8 解题报告——五十岚芒果酱

    1.鼎纹 [问题描述] 据说鼎纹的 种制造 式是 铜模印出来的,这是我国古代劳动 智慧 的结晶.铜模印过的地 ,会留下深深的印记,经过时间的炼化,洗 练成历史的遗存. 聪明的古代劳动人民拥有一个 a ...

  10. 冲刺Noip2017模拟赛7 解题报告——五十岚芒果酱

    1.二叉树(binary) .二叉树 (binary.cpp/c/pas) [问题描述] 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: ()若左子树不空,则左子树上所有结点的值均小于它的根结 ...

随机推荐

  1. AITCA联盟:渠道商的革命号角,产业变革的领航者!

    AITCA联盟:渠道商的革命号角,产业变革的领航者! 在AI技术风起云涌的今天,一场无声的革命正在悄然酝酿.在这场革命中,渠道商们不再是被动接受的附庸,而是即将成为改写产业规则.掌握自己命运的主宰者! ...

  2. 基于大模型的 RAG 核心开发——详细介绍 DeepSeek R1 本地化部署流程

    前言 自从 DeepSeek 发布后,对 AI 行业产生了巨大的影响,以 OpenAI.Google 为首的国际科技集团为之震惊,它的出现标志着全球AI竞争进入新阶段.从以往单纯的技术比拼转向效率.生 ...

  3. C#之集合常用扩展方法与Linq

    一.集合的常用扩展方法(lambda的方式) 1.Where() 根据条件选择数据 2.Select() 根据数据条件转换成新的数据类型,类似于DTO转换类 3.Max() 根据条件选择最大值 4.M ...

  4. 做Docx预览,一定要做这个神库!!

    Hey, 我是 沉浸式趣谈 本文首发于[沉浸式趣谈],我的个人博客 https://yaolifeng.com 也同步更新. 转载请在文章开头注明出处和版权信息. 如果本文对您有所帮助,请 点赞.评论 ...

  5. apisix~ApisixPluginConfig的使用

    1. ApisixPluginConfig 的作用 插件配置复用:将插件配置定义为独立的资源,供多个路由或服务引用. 解耦插件与路由:修改插件配置时,只需更新 ApisixPluginConfig,无 ...

  6. MySQL 中 InnoDB 存储引擎与 MyISAM 存储引擎的区别是什么?

    MySQL 中 InnoDB 存储引擎与 MyISAM 存储引擎是两种常见的存储引擎,它们在性能.事务支持.锁机制.数据完整性等方面存在一些显著的区别.以下是它们的主要区别: 1. 事务支持 Inno ...

  7. MySQL 中的 MVCC 是什么?

    MySQL 中的 MVCC 是什么? MVCC(Multi-Version Concurrency Control) 是 MySQL 数据库用来处理并发访问的技术,特别是在 InnoDB 存储引擎中, ...

  8. kettle介绍-Step之Value Mapper

    Value Mapper值映射介绍 值映射步骤是将字符串值从一个值映射为另一个值.值映射步骤提供了一个简单的替代方法,在输入流中选中一个字段,通过字段值设置源值和目标值,再将映射值输出给后续步骤使用. ...

  9. zabbix 之安装采坑记

    很久没有安装过zabbix,理论上应该是很简单,但是还是遇到好几个小问题,导致浪费了两个小时时间了要,特此记录一下 如果没有研发源码的能力,建议选择LTS版本 zabbix 4.0 官方安装文档: h ...

  10. php 二维数组转成一维数组

    // 1 $c = call_user_func('array_merge', $reds);// 2 $c = array_merge(...$reds);// 3 array_map(functi ...