【NOIP备考计划】Day3

一、今日计划 2021.10.28

今天没有考试。回顾了一下之前的考试情况,感觉挺多知识点、代码量都比较简单的题,自己考场没有拿满,是有点遗憾的,所以今天打算把之前模拟赛的题再做做。


模拟赛t1 (CF原题

1.Imbalanced Array    (顺便推一下我的洛谷博客:【单调栈】 - shengnan 的博客 )

题意:求一个序列所有子区间最大值减最小值之和

step0:暴力做法就是枚举左右端点,用st表求区间最值相减,即可算出每个区间的贡献,然后相加即可

发现:如果我们从每一个区间的贡献度考虑,那么需要枚举左右端点,O(n^2)起步,所以,直接求每个区间的贡献行不通

step1:再分析题目,寻找突破口:求每个区间的最值——实际上,可能有几个包含a[i]子区间的最大值都是a[i],也就是说,我们是否可以不必枚举每个区间求它的最大值,而是考虑每一个元素在哪些子区间是最大值(最小值同理)请仔细思考这一把求区间贡献转为求单个元素贡献的思考过程,这是复杂度从O(n^2)降为O(n)的关键

step2:考虑怎么求单个元素的贡献,也就是一个数a[i]在哪些范围内是该区间的最值?(下面以最大值为例,最小值同理)

所以,我们要找从a[i]开始往右第一个比a[i]大的数a[j],从a[i]开始往左第一个比a[i]大的数a[k],那么在a[j]到a[k]内的所有包含a[i]的子区间,最大值都是a[i],这可以用单调栈来实现。这样,左端点可以是在j~i任意一个,右端点可以是在i~k任意一个,根据乘法原理即可求出答案

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,a[1000005],top,s[1000005],l[1000005][2],r[1000005][2],ans;
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        while(top&&a[i]<a[s[top]])--top;
        l[i][0]=i-s[top];s[++top]=i;
    }
    memset(s,0,sizeof(s));top=0;s[0]=n+1;
    for(int i=n;i>=1;i--){
        while(top&&a[i]<=a[s[top]])top--;
        r[i][0]=s[top]-i;s[++top]=i;
    }
    memset(s,0,sizeof(s));top=0;s[0]=0;
    for(int i=1;i<=n;i++){
        while(top&&a[i]>=a[s[top]])top--;
        l[i][1]=i-s[top];s[++top]=i;
    }
    memset(s,0,sizeof(s));top=0;s[0]=n+1;
    for(int i=n;i>=1;i--){
        while(top&&a[i]>a[s[top]])top--;
        r[i][1]=s[top]-i;s[++top]=i;
    }
    for(int i=1;i<=n;i++)ans+=(l[i][1]*r[i][1])*a[i],ans-=(l[i][0]*r[i][0])*a[i];
    printf("%lld\n",ans);
    return 0;
}

模拟赛t2:树上的数  

 

#include <cstdio>
using namespace std;
int n,m,a,b,x,y,fa[5000005],q,cnt,head[5000005],res,ans;
bool vis[5000005];
struct node{int to,nxt;}e[5000005];
void insert(int u,int v){
	e[++cnt].nxt=head[u];e[cnt].to=v;head[u]=cnt;
}
void dfs(int u){
	if(vis[u])return;vis[u]=1;ans--;
	for(int i=head[u];i;i=e[i].nxt)dfs(e[i].to);
}
int main(){
	scanf("%d%d%d%d",&n,&m,&a,&b);fa[2]=1;insert(1,2);
	for(int i=3;i<=n;i++){
		fa[i]=((1ll*fa[i-1]*a+b)^19760817)%(i-1)+1;
		insert(fa[i],i);
	}
	scanf("%d%d%d",&q,&x,&y);ans=n;
	for(int i=1;i<=m;i++,q=(((1ll*q*x+y)^19760817)^(i<<1))%(n-1)+2)
		dfs(q),res^=ans;
	printf("%d\n",res);
	return 0;
}

 模拟赛t3 时代的眼泪

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,q,x,w[1000005],b[1000005],cnt,head[1000005];
ll c[1000005],ans[1000005],sz[1000005],siz[1000005],tmp[1000005],res[1000005];
struct node{int to,nxt;}e[2000005];
void insert(int u,int v){e[++cnt].nxt=head[u];e[cnt].to=v;head[u]=cnt;}
ll lowbit(int x){return x&(-x);}
void update(int x,int v){while(x<=n){c[x]+=v;x+=lowbit(x);}}
ll query(int x){ll res=0;while(x){res+=c[x];x-=lowbit(x);}return res;}
void dfs1(int u,int fa){
	siz[u]=query(w[u]-1);
	if(fa)sz[u]=query(w[fa]-1);
	for(int i=head[u],v;i;i=e[i].nxt){
		v=e[i].to;if(v==fa)continue;dfs1(v,u);
	}
	res[u]=query(w[u]-1)-siz[u];
	ans[1]+=res[u];
	update(w[u],1);
	if(fa)tmp[u]=query(w[fa]-1)-sz[u];
}
void dfs2(int u,int fa){
	if(u!=1){
		ans[u]=ans[fa]-tmp[u];
		ans[u]=ans[u]+query(w[u]-1)-res[u];
	}
	for(int i=head[u],v;i;i=e[i].nxt){
		v=e[i].to;if(v==fa)continue;dfs2(v,u);
	}
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)scanf("%d",&w[i]),b[i]=w[i];
	for(int i=1,x,y;i<=n-1;i++)scanf("%d%d",&x,&y),insert(x,y),insert(y,x);
	sort(b+1,b+1+n);int len=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++)w[i]=lower_bound(b+1,b+1+len,w[i])-b;
	dfs1(1,0);dfs2(1,0);
	for(int i=1;i<=q;i++)scanf("%d",&x),printf("%lld\n",ans[x]);
	return 0;
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值