[ARC177D] Earthquakes 题解

[ARC177D] Earthquakes

单调栈好题。

题面不短,给了我们很多限制。一定要理清思路,挨个来解决这些限制。

首先可以确定的是,先把所有电线杆按照位置而不是倒塌时间来排序。现在我们定义第 i 个电线杆是从左往右数第 i 个电线杆,每个电线杆的倒塌时间是 xi

容易发现,电线杆可以分成若干个段,使得同一个段内的电线杆之间的距离全部小于 H。这样的话段与段之间肯定是互不影响的,我们只需考虑同一个段内的情况,最后合并答案即可。

每个段内的情况

要使得这个段内的电线杆全部倒塌,考虑转化一下,求出第 i 个电线杆是最后主动倒塌的概率,其实和原问题是等价的。(“主动” 指不被其它电线杆碰倒的倒塌)

显然,一个段能被分成三部分:向左倒塌的一部分、站着的一部分、向右倒塌的一部分。

那么要使第 i 个电线杆最后主动倒下,当且仅当它是那站着的一部分的左端点或右端点。用官解的图来说就是类似这样(不过它只画了一个电线杆):

补充:下文所说 “前缀/后缀最小值数量” 的意义为:找到向左/向右第一个小于 xi 的位置,这个位置所代表的前缀/后缀长度。

这种情况等价于(设当前位置为 p):

  • i[1,p1],其前缀 1i 的最小值的位置都向左倒塌;
  • i[p+1,n],其后缀 in 的最小值的位置都向右倒塌。这里的 n 是这个段的电线杆数量。

所有前缀最小值和后缀最小值如果都按我们希望的方向倒塌,这个概率是 12cnt,其中 cnt 是这个点的前缀最小值和后缀最小值的数量之和。此外还有一些特殊情况需要处理。当 n=1 时,当前点向左还是向右倒无所谓,所以概率就是上面的那个东西;当 i=1 或 i=n 或当前点有某个相邻的点的 x 值比他大,当前点的倒塌方向是固定的,所以概率需要再乘 1/2

现在子问题就解决了。我们最终得到了每一段的每根电线杆最后一个倒塌的概率。

合并答案

一共有 tot 段,电线杆 i 所在段的编号为 idx(i),则第 t 个电线杆的答案就是:

s1×s2××sidx(t)1×res×sidx(t)+1××stot

其中 si 是第 i 段最后倒下的电线杆编号小于 t 的概率之和。

考虑按照时间逐个添加点,那这个问题就是一个单点加、区间求乘积的问题,上线段树即可。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
#define inv(x) power(x,MOD-2)
#define fs first
#define sc second
using namespace std;

char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
template<typename T>
void write(T x,char sf='\n'){
	if(x<0)putchar('-'),x=~x+1;
	int top=0;
	do str[top++]=x%10,x/=10;while(x);
	while(top)putchar(str[--top]+48);
	if(sf^'#')putchar(sf);
}
using ll=long long;
constexpr int MAXN=2e5+5;
constexpr ll MOD=998244353;
int n,h,idx[MAXN],tot,p[MAXN];
int stk[MAXN],top;
ll pw[MAXN]={1};
vector<pair<int,int>>v;
vector<int>a[MAXN];
vector<ll>res[MAXN];

ll power(ll a,int b){
	ll res=1;
	for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
	return res;
}
struct{
	#define lp p<<1
	#define rp p<<1|1
	ll st[MAXN<<2];
	void chg(int x,ll k,int s=1,int t=tot,int p=1){
		if(s==t) return st[p]=(st[p]+k)%MOD,void();
		int mid=(s+t)>>1;
		if(x<=mid) chg(x,k,s,mid,lp);
		else chg(x,k,mid+1,t,rp);
		st[p]=st[lp]*st[rp]%MOD;
	}
	ll ask(int l,int r,int s=1,int t=tot,int p=1){
		if(l<=s&&t<=r) return st[p];
		int mid=(s+t)>>1;
		if(l>mid) return ask(l,r,mid+1,t,rp);
		if(mid>=r) return ask(l,r,s,mid,lp);
		return ask(l,r,s,mid,lp)*ask(l,r,mid+1,t,rp)%MOD;
	}
}T;
void ins(int x,bool fl){
	if(fl) tot++;
	idx[x]=tot;
	a[tot].emplace_back(x);
	p[x]=a[tot].size()-1;
}
void solve(int id,vector<int>&a){
	static int cnt[MAXN];
	int n=a.size()-1;
	top=0;
	for(int i=1;i<=n;i++){
		while(top&&a[stk[top]]>a[i]) top--;
		cnt[i]=top;
		stk[++top]=i;
	}
	top=0;
	for(int i=n;i;i--){
		while(top&&a[stk[top]]>a[i]) top--;
		cnt[i]+=top;
		stk[++top]=i;
	}
	res[id].resize(1);
	for(int i=1;i<=n;i++){
		int tp=0;
		if(i==1||a[i-1]<a[i]) tp++;
		if(i==n||a[i+1]<a[i]) tp++;
		res[id].emplace_back(tp*pw[n-cnt[i]-1]%MOD);
	}
}

int main(){
	n=read(),h=read();
	for(int i=1;i<=n;i++){
		pw[i]=(pw[i-1]<<1)%MOD;
		v.emplace_back(read(),i);
		a[i].resize(1);
	}
	sort(v.begin(),v.end());
	ins(v.begin()->sc,1);
	for(auto it=v.begin()+1;it!=v.end();it++) ins(it->sc,it->fs-prev(it)->fs>h);
	for(int i=1;i<=tot;i++) solve(i,a[i]);
	for(int i=1;i<=n;i++){
		ll ans=1;
		if(idx[i]>1) ans=T.ask(1,idx[i]-1);
		if(idx[i]<tot) ans=ans*T.ask(idx[i]+1,tot)%MOD;
		ans=ans*res[idx[i]][p[i]]%MOD;
		T.chg(idx[i],res[idx[i]][p[i]]);
		write(ans,' ');
	}
	return putchar('\n'),fw,0;
}
posted @   Laoshan_PLUS  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示