\(\color{white}{\mathbb{荷花映日,莲叶遮天,名之以:残荷}}\)


今天再次翻车掉出前十

开题看错 \(t1\) 以为操作2的值固定发现是个简单题,然后 \(t2\) 开始大力 \(dp\) 两个小时还是过不了样例(后来发现是尽头情况处理错了),回头看 \(t1\) 发现看错题,重新想+写半个小时,最后二十分钟打了 \(t3\) 暴力,又开始调 \(t2\),直到结束也没有一个靠谱的输出

实践证明要先打好暴力(今天唯一得的分全是暴力分……)

难度判断失误,留给 \(t3\) 时间过少,而实际上 \(t3\) 更容易得到更高的分数


A. Dove 打扑克

由于每次合并都会减少一堆,所以哪怕最终每一堆个数都不一样,最多只有 \(\sqrt{n}\) 个

所以可以得出结论不同大小的堆的个数最多 \(\sqrt{n}\) 个

那么把这些存进数组里,只要保证每次操作是根号的,就可以保证在 \(m\sqrt{n}\) 的复杂度完成

统计答案时,可以用双指针维护,配合后缀和预处理,还可以保证根号复杂度


B. Cicada 与排序

对于每一个数处理其最终在每个位置的概率,再乘位置即可算出期望

设 \(g[i]\) 表示这个树到 \(i\) 位置的概率

考虑模拟归并排序的过程进行递归(只递归当前数该去的半个区间)

这样需要维护上一层向这一层的 \(g\) 数组的转移

首先维护一个 \(f\) 辅助 \(dp\) 值,\(f[i][j]\) 表示排序合并左右区间的时候左边的选到第 \(i\) 个数,此时右边选到第 \(j\) 个数的概率,这个很好转移,\(f[i][j]=(f[i-1][j]+f[i][j-1])/2\) 即可

考虑 \(g\) 通过 \(f\) 进行转移

对于 \(g[i+j]\),如果从 \(f[i][j]\) 转移会有一个问题:不能保证当前时刻选的是左区间的点

所以应该改为 \(f[i-1][j]/2\)

这样还有一个问题,如果右边已经到了尽头,那么其实左边向下一个移动的概率不再是 \(\frac{1}{2}\) 而变成了 \(1\)

那么相当于 \(\sum f[k][j-1]/2\)

这样总复杂度是 \(n^3\) 的

代码实现
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=505;
const int mod=998244353;
int n,f[maxn][maxn],g[maxn],h[maxn],a[maxn],b[maxn],posl,posr,pos[maxn],ans[maxn],inv2;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
int po(int a,int b=mod-2){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void solve(int l,int r,int pos,int w){
if(l==r){
g[l]=1;
return ;
}
for(int i=l;i<=r;i++)b[i]=a[i];
sort(b+l,b+r+1);
b[l-1]=0;
b[r+1]=0;
int st=0,ed=0;
for(int i=l;i<=r;i++){
if(b[i]!=b[i-1]&&b[i]==w)st=i;
if(b[i]!=b[i+1]&&b[i]==w)ed=i;
}
// if(w==3)cout<<st<<" "<<ed<<endl;
int mid=l+r>>1;
if(pos<=mid){
solve(l,mid,pos,w);
for(int i=l;i<=mid;i++)b[i]=a[i];
sort(b+l,b+mid+1);
b[l-1]=b[mid+1]=0;
for(int i=l;i<=mid;i++){
if(b[i]!=b[i-1]&&b[i]==w)posl=i;
if(b[i]!=b[i+1]&&b[i]==w)posr=i;
}
int num=0;
for(int i=mid+1;i<=r;i++)if(a[i]==w)num++; for(int i=0;i<=posr-posl;i++){
for(int j=0;j<=num-1;j++){
h[st+i+j]=(h[st+i+j]+g[posl+i]*f[i][j]%mod*inv2)%mod;
}
int sum=0;
if(!num)sum=1;
else{
for(int j=0;j<=i;j++)sum=(sum+f[j][num-1])%mod;
sum=sum*inv2%mod;
}
h[st+i+num]=(h[st+i+num]+g[i+posl]*sum)%mod;
} for(int i=l;i<=r;i++)g[i]=0;
for(int i=st;i<=ed;i++){
g[i]=h[i],h[i]=0;
// if(pos==1)cout<<l<<" "<<r<<" "<<i<<" "<<g[i]<<endl;
}
}
else{
solve(mid+1,r,pos,w);
for(int i=mid+1;i<=r;i++)b[i]=a[i];
sort(b+mid+1,b+r+1);
b[mid]=b[r+1]=0;
for(int i=mid+1;i<=r;i++){
if(b[i]!=b[i-1]&&b[i]==w)posl=i;
if(b[i]!=b[i+1]&&b[i]==w)posr=i;
} int num=0;
for(int i=l;i<=mid;i++)if(a[i]==w)num++; for(int i=0;i<=posr-posl;i++){
for(int j=0;j<=num-1;j++){
h[st+i+j]=(h[st+i+j]+g[posl+i]*f[i][j]%mod*inv2)%mod;
// if(pos==5)cout<<"hhh "<<st+i+j<<" "<<h[st+i+j]<<endl;
}
int sum=0;
if(!num)sum=1;
else{
for(int j=0;j<=i;j++)sum=(sum+f[j][num-1])%mod;
sum=sum*inv2%mod;
}
h[st+i+num]=(h[st+i+num]+g[i+posl]*sum)%mod;
// if(pos==5)cout<<"ppp "<<st+i+num<<" "<<h[st+i+num]<<" "<<i<<" "<<sum<<endl;
} // if(w==4)cout<<"ggg "<<l<<" "<<r<<" "<<st<<" "<<ed<<" "<<h[st]<<endl;
for(int i=l;i<=r;i++)g[i]=0;
for(int i=st;i<=ed;i++){
g[i]=h[i],h[i]=0;
// if(pos==5)cout<<"ggg "<<l<<" "<<r<<" "<<i<<" "<<g[i]<<endl;
}
}
return ;
}
void pre(){
f[0][0]=1;
for(int i=1;i<=n;i++)f[i][0]=f[i-1][0]*inv2%mod,f[0][i]=f[0][i-1]*inv2%mod;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j]=(f[i-1][j]+f[i][j-1])*inv2%mod;
// cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
}
}
return ;
}
signed main(){
// freopen("sort101.in","r",stdin);
// freopen("my.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
inv2=po(2);
pre();
for(int i=1;i<=n;i++){
memset(g,0,sizeof g);
memset(h,0,sizeof h);
solve(1,n,i,a[i]);
int ans=0;
for(int j=1;j<=n;j++){
// if(i==1)cout<<g[j]<<" ";
ans=(ans+j*g[j])%mod;
}
cout<<ans<<" ";
}
return 0;
}

C. Cicada 拿衣服

非常神奇的一道题

首先注意对于单个数 \(OR-AND=XOR\),但对于多个数不是这样的

用到一个性质,序列里前缀与和或的和最多变化 \(logn\) 次(因为每一位最多一次,不可能往回变)

再观察当右端点固定时,当左端点往左延伸时,\(max\) 单调不减,\(min\) 单调不增,那么总的值是递减时,只会在位运算突变时断崖式上升或下降一段

那么只要将位运算值相同的区间分成一小段一小段的,然后段内进行二分即可

如果直接二分是双 \(log\) 的,那么考虑二分前先判断区间最大值是否满足条件,如果满足再二分,且找到后立即停止

当插入一个新值时,之前的位运算块可能会合并,这个用链表 \(O(1)\) 维护即可

更新答案可以用线段树

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,po[25],lg[maxn*15],mx[maxn][25],mn[maxn][25],k,sum[maxn],a[maxn];
int pre[maxn],suf[maxn],l[maxn],r[maxn],hd,tl,val1[maxn],val2[maxn];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
void st_pre(){
int len=lg[n];
for(int j=1;j<=len;j++){
for(int i=1;i<=n;i++){
mx[i][j]=max(mx[i][j-1],mx[i+po[j-1]][j-1]);
mn[i][j]=min(mn[i][j-1],mn[i+po[j-1]][j-1]);
}
}
return ;
}
int ask_mx(int l,int r){
int len=lg[r-l+1];
return max(mx[l][len],mx[r-po[len]+1][len]);
}
int ask_mn(int l,int r){
int len=lg[r-l+1];
return min(mn[l][len],mn[r-po[len]+1][len]);
}
struct Seg{
int l,r,mx,lazy;
}t[maxn*4];
void build(int p,int l,int r){
t[p].l=l;
t[p].r=r;
if(l==r)return ;
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
return ;
}
void change(int p,int l,int r,int w){
if(t[p].l>=l&&t[p].r<=r){
t[p].mx=max(t[p].mx,w);
return ;
}
int mid=t[p].l+t[p].r>>1;
if(l<=mid)change(p<<1,l,r,w);
if(r>mid)change(p<<1|1,l,r,w);
return ;
}
void ask(int p){
t[p].mx=max(t[p].mx,t[p>>1].mx);
if(t[p].l==t[p].r){
if(!t[p].mx)cout<<"-1 ";
else printf("%d ",t[p].mx);
return ;
}
ask(p<<1);
ask(p<<1|1);
return ;
}
void update(int pos,int w){
for(int p=tl;p!=hd;p=pre[p]){
val1[p]|=w;
val2[p]&=w;
}
for(int p=tl;p!=hd&&pre[p]!=hd;p=pre[p]){
if(val1[p]-val2[p]==val1[pre[p]]-val2[pre[p]]){
pre[suf[p]]=pre[p];
suf[pre[p]]=suf[p];
r[pre[p]]=r[p];
if(p==tl)tl=pre[p];
}
}
if(tl==hd||val1[tl]!=w||val2[tl]!=w){
tl++;
pre[tl]=tl-1;
l[tl]=r[tl]=pos;
suf[tl-1]=tl;
suf[tl]=0;
val1[tl]=val2[tl]=w;
}
else r[tl]=pos;
return ;
}
bool check(int l,int r,int val1,int val2){
return ask_mn(l,r)-ask_mx(l,r)+val1-val2>=k;
}
void todoask(int p,int pos){
int ll=l[p],rr=r[p];
while(ll<rr){
int mid=ll+rr>>1;
// cout<<ll<<" "<<rr<<endl;
if(check(mid,pos,val1[p],val2[p]))rr=mid;
else ll=mid+1;
}
change(1,ll,pos,pos-ll+1);
return ;
}
void doask(int pos){
int i=0;
for(int p=suf[hd];p;p=suf[p]){
if(check(r[p],pos,val1[p],val2[p])){
todoask(p,pos);
return ;
}
// i++;
// if(i==70)return ;
// if(pos==412)cout<<p<<" "<<tl<<" "<<suf[p]<<endl;
if(p==tl)break;
}
}
int main(){
// freopen("naive5.in","r",stdin);
// freopen("my.out","w",stdout);
n=read();
k=read();
memset(mn,0x3f,sizeof mn); for(int i=1;i<=n;i++){
a[i]=read();
mx[i][0]=mn[i][0]=a[i];
} po[0]=1;
for(int i=1;i<=20;i++){
po[i]=po[i-1]*2;
for(int j=po[i-1];j<=po[i]-1;j++)lg[j]=i-1;
}
st_pre();
build(1,1,n);
// cout<<"hhh"<<endl;
for(int i=1;i<=n;i++){
update(i,a[i]);
doask(i);
// cout<<i<<endl;
} ask(1);
cout<<endl;
return 0;
}

\(\color{white}{\mathbb{小荷才露尖尖角,早有蜻蜓立上头。}}\)

noip模拟36的更多相关文章

  1. 2021.8.11考试总结[NOIP模拟36]

    T1 Dove玩扑克 考场并查集加树状数组加桶期望$65pts$实际$80pts$,考后多开个数组记哪些数出现过,只扫出现过的数就切了.用$set$维护可以把被删没的数去掉,更快. $code:$ 1 ...

  2. NOIP 模拟 $36\; \rm Cicada 与排序$

    题解 \(by\;zj\varphi\) 设 \(rk_{i,j}\) 表示第 \(i\) 个数最后在相同的数里排第 \(j\) 位的概率. 转移时用一个 \(dp\),\(dp_{i,j,0/1}\ ...

  3. Noip模拟36 2021.8.11

    刚题的习惯还是改不了,怎么办??? T1 Dove打扑克 考场上打的动态开点线段树+并查集,考后发现自己像一个傻子,并查集就行.. 这几天恶补数据结构疯了 用树状数组维护后缀和,$siz_i$表示编号 ...

  4. NOIP模拟 36

    又是sb错误丢rank1... T1加了一句特判,暴涨80分... 要不要这么残忍...我暴力其实打的很满的好吗QAQ T1 暴力写成$while(lim[j].id==i)$少写的特判是$(j< ...

  5. NOIP 模拟 $36\; \rm Cicada 拿衣服$

    题解 \(by\;zj\varphi\) 发现右端点固定时,左端点的 \(min-max\) 单调递减,且对于 \(or\) 和 \(and\) 相减,最多有 \(\rm2logn\)个不同的值,且相 ...

  6. NOIP 模拟 $36\; \rm Dove 打扑克$

    题解 \(by\;zj\varphi\) 引理 对于一个和为 \(n\) 的数列,不同的数的个数最多为 \(\sqrt n\) 证明: 一个有 \(n\) 个不同的数的数列,和最小就是 \(n\) 的 ...

  7. 「题解」NOIP模拟测试题解乱写II(36)

    毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...

  8. NOIP模拟赛-2018.11.6

    NOIP模拟赛 今天想着反正高一高二都要考试,那么干脆跟着高二考吧,因为高二的比赛更有技术含量(我自己带的键盘放在这里). 今天考了一套英文题?发现阅读理解还是有一些困难的. T1:有$n$个点,$m ...

  9. Nescafe #29 NOIP模拟赛

    Nescafe #29 NOIP模拟赛 不知道这种题发出来算不算侵权...毕竟有的题在$bz$上是权限题,但是在$vijos$似乎又有原题...如果这算是侵权的话请联系我,我会尽快删除,谢谢~ 今天开 ...

随机推荐

  1. 几百行代码写个Mybatis,原理搞的透透的!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 Mybatis 最核心的原理也是它最便于使用的体现,为什么这说? 因为我们在使用 M ...

  2. js 倒计时退出

    <p>您已提交成功!<span id='submit_second'></span></p> $("#submit_second") ...

  3. SpringBoot整合Guacamole教程

    前言 本文主要介绍的是SpringBoot如何整合Guacamole在浏览器是远程桌面的访问. Guacamole 介绍 Apache Guacamole 是一个无客户端远程桌面网关.它支持标准协议, ...

  4. Maven-内部多个项目依赖自动升级版本的部署

    需要自动升级版本的AAA项目发布 (有内部依赖时) 步骤比较复杂, 有一些需要根据实际情况调整. 考虑了以下几种可能性: 依赖模块的版本有更新 依赖模块版本没更新 依赖模块的版本号: 直接定义, 用属 ...

  5. .NET Conf 2020大会将于2020年11月10日--- 11月12日举行 (UTC)时区

    .NET Conf 2020大会将于2020年11月10日--- 11月12日举行 (UTC)时区 开始时间 2020年11月10日 08:00 (PT) | 16:00 (UTC)| 24:00(北 ...

  6. Java流程控制04——Switch选择结构

    switch 多选择结构 switch case 语句判断一个变量与一系列值中某个值是否相等,每个支撑位一个分支. switch语句中的变量类型可以是: byte short int 或者 char ...

  7. 构建工具之Maven的使用(一)

    一.前言 对于开发一个Java项目,上线之前会通过编译,测试,打包,部署这几个构建过程,如果文件较少,我们可以使用java-->javac-->jar这些命令去完成上述的构建流程.但是当工 ...

  8. sql server时间转换

    --getdate 获取当前时间 select getdate() --dateadd 原有时间加: 2013-02-17 13:20:16 此时间加12个月 select dateadd(MONTH ...

  9. Asp.Net Core Razor页面中使用echarts展示图形

    Asp.Net Core Razor页面中使用echarts展示图形 要在Razor页面中使用echarts显示图形,主要问题点在于如何将数据传递给js文件. 1,下载安装echarts库文件 首先引 ...

  10. 【XSS-labs】level 6-10

    Level 6 查看源码:对URL中的传参进行了HTML实体化转义,搜索框中的值对 src\onxxxxx\data\href进行了限制. 采用大小写绕过,在搜索框输入payload,注意闭合inpu ...