考试过程:

刚看完题,发现T1是个类lis 问题,但要求$O(nlogn)$,应该是个数据结构优化dp,T2应该是个数据结构,T3是个字符串?没有匹配,不会是后缀数组吧,这是NOIP模拟啊,可能是个dp。

开始分析T1,一开始想错了,以为只要一个树状数组就可以,是个全场切的sb题,后来发现过不了样例,后来发现是假的,然后默默的开始打$O(n^3)$暴力dp,半个小时后过了两个样例,60应该是稳了,怕后面题时间不够,就先看了后两题,第二题除了$O(n^2)$暴力啥也不会,T3觉得dp好像很可做,然后开始推,但一直想不到怎么去重,浪费了好多时间,最后T2打了暴力,T3根本没动。

觉得100+还是挺稳的,点开成绩,woc T1 CE!!! T2 WA0 艹,为什么啊。

我为什么要手贱手打max阿,还不编译。T2测试点分治 没return 0; 我@%$^*#&%^@#......

感觉考试的时候不太认真,不然也不会犯这么sb的错误,本来拿分就拿不过别人,还犯这种低级错误,感觉那天白跟教练谈了,真tm是不长记性。

题解:

T1 队长快跑

线段树优化dp,考场上打的是$O(n^3)$ 的,然后觉得优化空间不大,就没有往细里想,其实暴力完全可以做到$O(n^2)$。

然后线段树优化就比较显然了然而我蒟蒻就是想不到啊。

设$dp_{i,j}$表示处理完$i$个水晶并且当$a[i]$最小值为$j$的最优解

那么我们进行分类讨论

如果$a[i]<b[i]$ 那么$dp_{i,a[i]}=\max{dp_{i,j}}+1,j>=b[i]+1,j<=MAX$

否则$dp_{i,a[i]}=\max{dp_{i,a[i]}},j>=a[i]+1,j<=MAX $

  $dp_{i,j}=dp_{i,j-1}+1,j>b[i],j<=a[i]$

那么我们只要把第二维放到线段树上维护一下就好了

这样就只需要一颗支持区间修改,单点修改,区间查询就好了。

 #include<bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N=1e5+;
int a[N],b[N];
int c[N<<];
int n;
int t=,len,q_mx;
struct SegmentTree{
int l,r,mx,lazy;
}tr[N<<];
int max(int a,int b){
return a>b?a:b;
}
void down(int p){
if(!tr[p].lazy) return ;
tr[p<<].lazy+=tr[p].lazy;
tr[p<<|].lazy+=tr[p].lazy;
tr[p<<].mx+=tr[p].lazy;
tr[p<<|].mx+=tr[p].lazy;
tr[p].lazy=;
} void build(int p,int l,int r){
tr[p].l=l,tr[p].r=r;
if(l==r){
tr[p].mx=;
return ;
}
int mid=(l+r)>>;
build(p<<,l,mid);
build(p<<|,mid+,r);
tr[p].mx=max(tr[p<<].mx,tr[p<<|].mx);
}
void update(int p,int ll,int rr,int l,int r,int val){
if(ll<=l&&r<=rr){
tr[p].lazy+=val;
tr[p].mx+=val;
return ;
}
down(p);
int mid=(tr[p].l+tr[p].r)>>;
if(ll<=mid) update(p<<,ll,rr,l,mid,val);
if(rr>mid) update(p<<|,ll,rr,mid+,r,val);
tr[p].mx=max(tr[p<<].mx,tr[p<<|].mx);
}
void modify(int p,int l,int r,int pos,int val){
if(l==r){//cout<<val<<endl;
tr[p].mx=max(tr[p].mx,val);
// cout<<tr[p].mx<<endl;
return ;
}
down(p);
int mid=(tr[p].l+tr[p].r)>>;
if(pos<=mid) modify(p<<,l,mid,pos,val);
else modify(p<<|,mid+,r,pos,val);
tr[p].mx=max(tr[p<<].mx,tr[p<<|].mx);
}
void query(int p,int ll,int rr,int l,int r){
if(ll<=l&&r<=rr){//cout<<"tr=="<<tr[p].mx<<" mx=="<<q_mx<<endl;
q_mx=max(tr[p].mx,q_mx);
return ;
}
down(p);
int mid=(tr[p].l+tr[p].r)>>;
if(ll<=mid) query(p<<,ll,rr,l,mid);
if(rr>mid) query(p<<|,ll,rr,mid+,r);
}
int main(){
// freopen("leader.in","r",stdin);
// freopen("leader.out","w",stdout);
scanf("%d",&n);
int Max=;
for(int i=;i<=n;++i){
scanf("%d%d",&a[i],&b[i]);
c[++t]=a[i];
c[++t]=b[i];
Max=max(Max,a[i]);
}
sort(c+,c+t+);
len=unique(c+,c+t+)-c-;
for(int i=;i<=n;++i) {
a[i]=lower_bound(c+,c+len+,a[i])-c;
b[i]=lower_bound(c+,c+len+,b[i])-c;
}
// for(int i=1;i<=n;++i) cout<<a[i]<<" ";
// cout<<endl;
// for(int i=1;i<=n;++i) cout<<b[i]<<" ";
// for(int i=1;i<=n;++i) cout<<a[i]<<" "<<b[i]<<endl;
build(,,len);
for(int i=;i<=n;++i){
q_mx=;
if(a[i]<=b[i]){
query(,b[i]+,len,,len);
modify(,,len,a[i],q_mx+);
}
else{
query(,a[i]+,len,,len);
update(,b[i]+,a[i],,len,);
modify(,,len,a[i],q_mx+);
}
// cout<<"q_mx=="<<q_mx<<endl;
}
printf("%d",tr[].mx);
}

leader

T2 影魔

大神数据结构题

不难想到的是对于每一个节点开线段树但我就是想不到。

但是要维护什么,就看它要干什么,我们考虑把颜色和深度联系到一起,考虑维护在每颗子树内,每种颜色出现的最小深度,但是这样我们处理不了询问,所以我们再维护在每个子树内以每个深度为最小深度的颜色种数,这样我们把第二棵线段树可持久化就能做到询问深度为的以内的询问了。

但是这样空间会爆,所以两棵线段树都要动态开点。

显然这两个信息都满足可加性,只要在dfs的过程中向上合并就好了。

还有要注意的一点就是在合并第一棵线段树是会影响到第二棵线段树的信息,即在合并第一棵线段树时把深度较大的信息在相应颜色的第二棵线段树里把贡献减掉,细节还是蛮多的,蒟蒻博主打了一个下午,主要还是对动态开点线段树,可持久化线段树理解的不透彻。

复杂度$O(nlogn)$,但常数超级大,在线的。

强烈谴责两个log过的

 #include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
int tot;
int vis[N],d[N],c[N];
int fa[N];
int first[N],to[N<<],nex[N<<],cnt;
int root1[N*],root2[N*];
struct Seg1{
int l,r,val;
}tr[N*];
int n;
void build(int &x,int l,int r,int pos,int val){//root, l,r fanwei ,pos weizhi ,val quanzhi
x=++tot;
if(l==r){
tr[x].val=val;
return ;
}
int mid=(l+r)>>;
if(pos<=mid) build(tr[x].l,l,mid,pos,val);
else build(tr[x].r,mid+,r,pos,val);
} void update(int &x,int l,int r,int pos,int val){
++tot;
tr[tot]=tr[x];
x=tot;
tr[x].val+=val;
if(l==r) return ;
int mid=(l+r)>>;
if(pos<=mid) update(tr[x].l,l,mid,pos,val);
else update(tr[x].r,mid+,r,pos,val);
} int merge1(int x,int y,int l,int r,int p){//col xiabiao val shendu
if(!x||!y) return x+y;//chuan p yuanyinshi yaodui tr2 xiugai yi p weigen
int rt=++tot;
if(l==r){
tr[rt].val=min(tr[x].val,tr[y].val);
update(root2[p],,n,max(tr[x].val,tr[y].val),-);
return rt;
}
int mid=(l+r)>>;
tr[rt].l=merge1(tr[x].l,tr[y].l,l,mid,p);
tr[rt].r=merge1(tr[x].r,tr[y].r,mid+,r,p);
return rt;
}
int merge2(int x,int y,int l,int r){//dep xiabiao val col
if(!x||!y) return x+y;
int rt=++tot;
/*if(l==r){
tr[rt].val=tr[x].val+tr[y].val;
return rt;
}*/
tr[rt].val=tr[x].val+tr[y].val;
int mid=(l+r)>>;
tr[rt].l=merge2(tr[x].l,tr[y].l,l,mid);
tr[rt].r=merge2(tr[x].r,tr[y].r,mid+,r);
return rt;
}
int query(int p,int l,int r,int ll,int rr){
if(ll<=l&&r<=rr) return tr[p].val;
int ans=,mid=(l+r)>>;
if(ll<=mid) ans+=query(tr[p].l,l,mid,ll,rr);
if(rr>mid) ans+=query(tr[p].r,mid+,r,ll,rr);
return ans;
} void add(int a,int b){
to[++cnt]=b,nex[cnt]=first[a],first[a]=cnt;
}
void pre_dfs(int x,int fa){//cout<<x<<endl;
build(root1[x],,n,c[x],d[x]);
update(root2[x],,n,d[x],);
for(int i=first[x];i;i=nex[i]){
int y=to[i];
if(y==fa) continue;
d[y]=d[x]+;
pre_dfs(y,x);
root1[x]=merge1(root1[x],root1[y],,n,x);
root2[x]=merge2(root2[x],root2[y],,n);
}
} int main(){
int m;
scanf("%d%d",&n,&m);
int Max=;
for(int i=;i<=n;++i) {scanf("%d",&c[i]);Max=max(Max,c[i]);}
for(int i=;i<n;++i){
scanf("%d",&fa[i]);
add(fa[i],i+);
}
d[]=;
pre_dfs(,);
// cout<<endl;
// cout<<tot<<endl;
// for(int i=1;i<=n;++i) cout<<root1[i]<<" ";
// cout<<endl;
// for(int i=1;i<=n;++i) cout<<root2[i]<<" ";
for(int i=;i<=m;++i){
int x,dis;
scanf("%d%d",&x,&dis);
int ans=query(root2[x],,n,d[x],d[x]+dis);
printf("%d\n",ans);
}
}

shadow

T3 抛硬币

其实还是不太难的dp,不难想到设$dp[i][j]$表示处理完位置i且长度为j的方案数

很显然的转移:

$dp[i][j]=dp[i-1][j]+dp[i-1][j-1]$,但是有好多重的,所以想怎么去重

显然一个字符只会和他一样的字符产生重复,所以我们预处理每个字符和他相同的前驱,设其为$p[i]$,那么在减掉发$dp[p[i]-1][j-1]$就好了。

需要稍预处理,细节见代码。

 #include<bits/stdc++.h>
#define cout cerr
#define int long long
using namespace std;
const int N=;
const int mod=;
int f[N][N];
char s[N];
int p[N];
int sum[N],vis[N];
signed main(){
int l;
scanf("%s",s+);
scanf("%lld",&l);
int n=strlen(s+);
for(int i=;i<=n;++i){
sum[i]=sum[i-];
if(!vis[s[i]-'a']) sum[i]++;
vis[s[i]-'a']=;
}
for(int i=;i<=n;++i) f[i][]=sum[i];
for(int i=;i<=n;++i) p[i]=;
for(int i=;i<=n;++i){
for(int j=i-;j>=;--j){
if(s[j]==s[i]) {p[i]=j;break;}
}
}
// for(int i=1;i<=n;)
// for(int i=1;i<=n;++i) cout<<sum[i]<<" ";
// for(int i=1;i<=n;++i) cout<<p[i]<<" ";
for(int i=;i<=n;++i){
for(int j=;j<=min(l,i);++j){
f[i][j]=(f[i-][j]+f[i-][j-]-f[p[i]-][j-]+mod)%mod;
}
}
/*for(int i=1;i<=n;++i){
for(int j=1;j<=min(i,l);++j){
cout<<"dp["<<i<<"]["<<j<<"]="<<f[i][j]<<endl;
// if(p[i]-1==0)cout<<f[p[i]-1][j-1]<<" ";
}
}*/
printf("%lld",f[n][l]);
}

coin

20190908 NOIP 模拟40的更多相关文章

  1. Noip模拟40 2021.8.15

    T1 送花 按照题解意思说是扫描线题,但我觉得像一个线段树优化$dp$ 主要思想一样,就是暴力枚举右端点,同时维护左端点的最值, 考虑两种情况, 如果左端点在$r$扫到的数$i$上一次出现的位置之前, ...

  2. 2021.8.15考试总结[NOIP模拟40]

    T1 送花 线段树.枚举右端点,线段树记录左端点对应的值. 每次对当前颜色上上次出现的位置到上次出现的位置区间减,上次出现的位置到当前位置区间加. $code:$ 1 #include<bits ...

  3. NOIP模拟 40

    考得更嘛也不是了. 不过如果不犯任何低错的话.. T1 我神奇地想要缩减码量 比如想把尽量多的$b[i]-1$省掉 于是求$b[i]$的时候先减了个一 本来是正的 减完就忘了他应该是非负的了 于是线段 ...

  4. noip模拟40

    \(\color{white}{\mathbb{名之以:海棠}}\) 考场 \(t1\) 看见题意非常简单,觉得可能是个简单题 暴力算出几个小样例右端点右移的时候左端点都是单调右移的,以为具有单调性, ...

  5. [考试总结]noip模拟40

    最近真的是爆炸啊... 到现在还是有不少没改出来.... 所以先写一下 \(T1\) 的题解.... 送花 我们移动右端点,之后我们用线段树维护全局最大值. 之后还要记录上次的位置和上上次的位置. 之 ...

  6. NOIP模拟赛 by hzwer

    2015年10月04日NOIP模拟赛 by hzwer    (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...

  7. 11.7 NOIP模拟赛

    目录 2018.11.7 NOIP模拟 A 序列sequence(two pointers) B 锁lock(思路) C 正方形square(埃氏筛) 考试代码 B C 2018.11.7 NOIP模 ...

  8. NOIP模拟赛-2018.11.6

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

  9. 2016-06-19 NOIP模拟赛

          2016-06-19 NOIP模拟赛 by coolyangzc 共3道题目,时间3小时 题目名 高级打字机 不等数列 经营与开发 源文件 type.cpp/c/pas num.cpp/c ...

随机推荐

  1. Python基础『一』

    内置数据类型 数据名称 例子 数字: Bool,Complex,Float,Integer True/False; z=a+bj; 1.23; 123 字符串: String '123456' 元组: ...

  2. 编写函数模拟strcpy()函数功能

    strcpy(字符数组1,字符串2) strcpy( )用于将字符串2复制到字符数组1中 /* strcpy(字符数组1,字符串2) strcpy( )用于将字符串2复制到字符数组1中 模拟strcp ...

  3. Istio技术与实践02:源码解析之Istio on Kubernetes 统一服务发现

    前言 文章Istio技术与实践01: 源码解析之Pilot多云平台服务发现机制结合Pilot的代码实现介绍了Istio的抽象服务模型和基于该模型的数据结构定义,了解到Istio上只是定义的服务发现的接 ...

  4. C# 语言基础学习路线图

    一直以来,对于很多知识点都是存于收藏夹中,随着时间地变更,收藏夹中链接也起来越多,从未进行整理,也很零散,所以想对曾经遇到并使用过的一些知识形成文档,作为个人知识库的一部分. 就从C# 语言基础开始, ...

  5. sqlserver跨库操作数据

    垮库只能读操作,写操作需要设置权限. USE [jdddb] GO /****** Object: StoredProcedure [dbo].[proc_LYOrderCancel] Script ...

  6. Google自动打印

    浏览器打印功能,有很多小伙伴可能不太清楚,这里我们可以学习一下. 情景:开发一个需要打印小票的项目.(在订单页里,给客户添加一个打印的操作) 1.假设打印机已经连接好了 2.我们这一节用的浏览器是Go ...

  7. JavaSpring【三、Bean】

    配置项 id bean的标识 class bean的类全名 scope bean的作用域 constructor-arg 构造注入 properties 设值注入 autowire 装配模式 lazy ...

  8. linux后台启动项目命令

    在用xshell启动一个项目后,关闭了xshell后,项目又停止了 nohup python admin.py runserver & nohup ........  &   中间包含 ...

  9. web应用原理之——会话

    会话是大家开发Java EE Web应用的常用技术,那么会话是什么,会话的用途还有工作原理又是什么,下面就简单说一说. 什么是会话,在web应用中,作为客户端的浏览器,通过请求/响应这种模式访问同一个 ...

  10. CAFFE(三):Ubuntu下Caffe框架安装(仅仅Caffe框架安装)

    步骤一. 从github上下载(克隆)安装包 1.1 在你要安装的路径下 clone 此处我直接安装到home目录,执行: ~$ cd ~ 2 :~$ git clone https://github ...