20190908 NOIP 模拟40
考试过程:
刚看完题,发现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的更多相关文章
- Noip模拟40 2021.8.15
T1 送花 按照题解意思说是扫描线题,但我觉得像一个线段树优化$dp$ 主要思想一样,就是暴力枚举右端点,同时维护左端点的最值, 考虑两种情况, 如果左端点在$r$扫到的数$i$上一次出现的位置之前, ...
- 2021.8.15考试总结[NOIP模拟40]
T1 送花 线段树.枚举右端点,线段树记录左端点对应的值. 每次对当前颜色上上次出现的位置到上次出现的位置区间减,上次出现的位置到当前位置区间加. $code:$ 1 #include<bits ...
- NOIP模拟 40
考得更嘛也不是了. 不过如果不犯任何低错的话.. T1 我神奇地想要缩减码量 比如想把尽量多的$b[i]-1$省掉 于是求$b[i]$的时候先减了个一 本来是正的 减完就忘了他应该是非负的了 于是线段 ...
- noip模拟40
\(\color{white}{\mathbb{名之以:海棠}}\) 考场 \(t1\) 看见题意非常简单,觉得可能是个简单题 暴力算出几个小样例右端点右移的时候左端点都是单调右移的,以为具有单调性, ...
- [考试总结]noip模拟40
最近真的是爆炸啊... 到现在还是有不少没改出来.... 所以先写一下 \(T1\) 的题解.... 送花 我们移动右端点,之后我们用线段树维护全局最大值. 之后还要记录上次的位置和上上次的位置. 之 ...
- NOIP模拟赛 by hzwer
2015年10月04日NOIP模拟赛 by hzwer (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...
- 11.7 NOIP模拟赛
目录 2018.11.7 NOIP模拟 A 序列sequence(two pointers) B 锁lock(思路) C 正方形square(埃氏筛) 考试代码 B C 2018.11.7 NOIP模 ...
- NOIP模拟赛-2018.11.6
NOIP模拟赛 今天想着反正高一高二都要考试,那么干脆跟着高二考吧,因为高二的比赛更有技术含量(我自己带的键盘放在这里). 今天考了一套英文题?发现阅读理解还是有一些困难的. T1:有$n$个点,$m ...
- 2016-06-19 NOIP模拟赛
2016-06-19 NOIP模拟赛 by coolyangzc 共3道题目,时间3小时 题目名 高级打字机 不等数列 经营与开发 源文件 type.cpp/c/pas num.cpp/c ...
随机推荐
- 【Python】**kwargs和takes 1 positional argument but 2 were given
Python的函数定义中可以在参数里添加**kwargs——简单来说目的是允许添加不定参数名称的参数,并作为字典传递参数.但前提是——你必须提供参数名. 例如下述情况: class C(): def ...
- logstash grok
input { file { path => "/opt/service/test-service/logs/catalina-error*.log" type => ...
- 认识函数(python)
一般的函数都是有参数的,函数的参数都是放在函数定义的括号里的,函数参数的命名规则和我们说的变量的命名规则基本一样,一定要清晰明了.(能概括出它的意义,让人阅读你的代码,就知道这个参数是干嘛的就行).当 ...
- Python中的with语句(上下文管理协议)
在平时工作中总会有这样的任务,它们需要开始前做准备,然后做任务,然后收尾清理....比如读取文件,需要先打开,读取,关闭 这个时候就可以使用with简化代码,很方便 1.没有用with语句 f = o ...
- Payload 实现分离免杀
众所周知,目前的杀毒软件的杀毒原理主要有三种方式,一种基于特征,一种基于行为,一种基于云查杀,其中云查杀的一些特点基本上也可以概括为特征码查杀,不管是哪一种杀毒软件,都会检查PE文件头,尤其是当后门程 ...
- C语言两个特别大的整数类型相加超出范围使用两个技巧
技巧1:用long (%ld)或者long long(%lld)类型存取 技巧2:当两个同号的数字相加,放到等号的另一边,变成减号 问题: 给定区间[-2的31次方, 2的31次方]内的3个整数A.B ...
- 给枚举定义DescriptionAttribute
在C#中,枚举用来定状态值很方便,例如我定义一个叫做Season的枚举 public enum Season { Spring = 1, Summer = 2, Autumn = 3, Winter ...
- 【js】null 和 undefined的区别?
1.首先看一个判断题:null和undefined 是否相等 console.log(null==undefined)//true console.log(null===undefin ...
- JS ES7
指数运算符(幂) ** console.log(3 ** 2); //9 判断数组中包含 Array.prototype.includes(value) var arr1 = [1,2,'a','b' ...
- 什么是TCP/IP协议和举例
一张图+说明,彻底理解TCP/IP协议是个什么玩意儿! 相关图片:https://www.cnblogs.com/andy9468/p/10096606.html 说明:1.应用层常见协议或命令:HT ...