Loading

7367. 【2021.11.10NOIP提高组联考】可笑的 wenhao801

Description

\(n\) 列无助的袋鼠并排摆放在平面直角坐标系内,每个无助的袋鼠可以近似看成一个 \(1 \times 1\) 的正方形, 第 \(i\) 列无助的袋鼠在 \(x=i-1\)\(x=i\) 之间,从 \(x\) 轴开始向上摆放,其中第 \(i\) 列无助的袋鼠高度为 \(a_{i}\) (即这一列有 \(a_{i}\) 个无助的袋鼠),保证所有 \(a_{i}\) 互不相同

空中有 \(m\) 个可笑的 wenhao801,每个可笑的 wenhao801 也可以看成一个 \(1 \times 1\) 的正方形,第 \(i\) 个可 笑的 wenhao801 坐标为 \(\left(x_{i}, y_{i}\right)\) ,表示这个可笑的 wenhao801 在第 \(x_{i}\) 列无助的袋鼠上方,且高度为 \(y_{i}\) (高度定义为上底的 \(y\) 坐标),保证可笑的 wenhao801 不与无助的袋鼠重合,但两个可笑的 wenhao801 可能重合

定义一个无序可笑的 wenhao801 对 \((p, q)(p \neq q)\) 是「黑暗的」当且仅当从其中高度更高的可笑的 wenhao801 存在一条不升的路径到达高度更低的可笑的 wenhao801,并且不经过任何无助的袋鼠。

每个可笑的 wenhao801 有一个体重 \(w_{i}\) ,定义一个可笑的 wenhao801 对 \((p, q)\) 的权值为他们体重差的 平方,即 \(\left(w_{p}-w_{q}\right)^{2}\)

求所有「黑暗的」可笑的 wenhao801 对的权值和,答案对 \(998244353\)​ 取模。

\(1\le n,m\le 5\times 10^5\)

Solution

先求出每个点最左能到哪一列,最右能到哪一列,这个可以通过二分+ST 表来实现。

对于一个点 \((x_i,y_i)\),我们可以求出他左边的有贡献的点和右边的有贡献的点,计算答案可以用线段树。

考虑好做一点,先将点按照高度为第一关键字排序,每次对于一个点,先求他的答案在将他插入线段树中,这样可以保证产生权值的点都是符合高度限制的。

线段树记录 3 个值,个数,一次方和,二次方和。至于为什么,将 \((w_i-w_j)^2\) 展开就知道了。

Code

#include<cmath>
#include<cstdio>
#include<algorithm>
#define N 500005
#define mod 998244353
#define ll long long
using namespace std;
struct node
{
	ll x,y,v;
}p[N];
struct seg
{
	ll num,sig,mi;
}tree[N<<2];
ll n,m,l,r,mid,res,ans,h[N],lt[N],rt[N],f[N<<1][20];
bool cmp(node x,node y)
{
	if (x.y<y.y) return true;
	if (x.y>y.y) return false;
	return x.x<=y.x;
}
ll get(ll x,ll y)
{
	ll lg=log2(y-x+1);
	return max(f[x][lg],f[y-(1<<lg)+1][lg]);
}
void modify(ll now,ll l,ll r,ll p,ll v)
{
	if (l==r)
	{
		tree[now].num++;
		tree[now].mi=(tree[now].mi+v*v)%mod;
		tree[now].sig=(tree[now].sig+v)%mod;
		return;
	}
	ll mid=(l+r)>>1;
	if (p<=mid) modify(now<<1,l,mid,p,v);
	else modify(now<<1|1,mid+1,r,p,v);
	tree[now].num=tree[now<<1].num+tree[now<<1|1].num;
	tree[now].mi=(tree[now<<1].mi+tree[now<<1|1].mi)%mod;
	tree[now].sig=(tree[now<<1].sig+tree[now<<1|1].sig)%mod;
}
ll query(ll now,ll l,ll r,ll p,ll q,ll ty)
{
	if (p>q) return 0;
	if (l>=p&&r<=q)
	{
		if (ty==1) return tree[now].num;
		if (ty==2) return tree[now].sig;
		if (ty==3) return tree[now].mi;
	}
	ll mid=(l+r)>>1;
	if (q<=mid) return query(now<<1,l,mid,p,q,ty);
	else if (p>mid) return query(now<<1|1,mid+1,r,p,q,ty);
	else return (query(now<<1,l,mid,p,q,ty)+query(now<<1|1,mid+1,r,p,q,ty))%mod;
}
int main()
{
	freopen("flame.in","r",stdin);
	freopen("flame.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for (ll i=1;i<=n;++i)
		scanf("%lld",&h[i]),f[i][0]=h[i];
	h[0]=h[n+1]=mod;
	for (ll j=1;j<=19;++j)
		for (ll i=1;i<=n;++i)
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
	for (ll i=1;i<=m;++i)
		scanf("%lld%lld%lld",&p[i].x,&p[i].y,&p[i].v);
	sort(p+1,p+m+1,cmp);
	for (ll i=1;i<=m;++i)
	{
		l=0;r=p[i].x-1;res=0;
		while (l<=r)
		{
			mid=(l+r)>>1;
			if (get(mid,p[i].x-1)>=p[i].y) res=mid,l=mid+1;
			else r=mid-1;
		}
		lt[i]=res;
		l=p[i].x+1;r=n+1;res=n+1;
		while (l<=r)
		{
			mid=(l+r)>>1;
			if (get(p[i].x+1,mid)>=p[i].y) res=mid,r=mid-1;
			else l=mid+1;
		}
		rt[i]=res;
		lt[i]++;rt[i]--;
	}
	for (ll i=1;i<=m;++i)
	{
		ll num=query(1,1,n,lt[i],p[i].x,1),sig=query(1,1,n,lt[i],p[i].x,2),mi=query(1,1,n,lt[i],p[i].x,3);
		ans=(ans+num*p[i].v%mod*p[i].v%mod)%mod;
		ans=(ans-(2*sig*p[i].v%mod)+mod)%mod;
		ans=(ans+mi)%mod;
		num=query(1,1,n,p[i].x+1,rt[i],1),sig=query(1,1,n,p[i].x+1,rt[i],2),mi=query(1,1,n,p[i].x+1,rt[i],3);
		ans=(ans+num*p[i].v%mod*p[i].v%mod)%mod;
		ans=(ans-(2*sig*p[i].v%mod)+mod)%mod;
		ans=(ans+mi)%mod;
		modify(1,1,n,p[i].x,p[i].v);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-11-10 21:27  Thunder_S  阅读(94)  评论(0编辑  收藏  举报