[bzoj5040][dsu on tree]未来研究

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rose_max/article/details/82772747

Description

GX国未来研究的第一人–Hfu教授,最近获得了一份被认为是未来LY国的住民写下的日记。Hfu教授为了通过这份日
记来研究未来LY国的生活,开始着手调查日记中记载的事件。日记中记录了连续N天发生的时间,大约每天发生一
件。事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。Hfu教
授决定用如下的方法分析这些日记:

  1. 选择日记中连续的一些天作为分析的时间段
  2. 事件种类t的重要度为t*(这段时间内为t的事件数)
  3. 计算出所有事件种类的重要度,输出其中的最大值 现在你被要求制作一个帮助Hfu教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

Input

第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1…XN,Xi表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。
询问保证不会存在Ai<Aj<Bi<=Bj的情况,且对于任意的i,j不会有Bi=Aj,Ai=Aj。 1<=N<=5510^4
1<=Q<=4
10^5 1<=Xi<=10^9 (1<=i<=N)

Output

输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度

Sample Input

5 4

9 8 7 8 5

1 2

3 5

4 4

5 5

Sample Output

9

8

8

5

题解

dsu on tree的裸题…
我是在这里学的!
大概思想
每次子树操作的时候 暴力扫子树是 n 2 n^2
我们每次最后扫重儿子那棵树 并且不把重儿子的答案清空 直接将他作为当前这棵子树的答案,再暴力扫其它轻儿子加上贡献
那么再说这题
其实就是历史研究的加强版and卡常版
注意到题目中有一个限制
保证没有A[i]<A[j]<B[i]<=B[j]
也就是说任意两个询问,只可能有包含或者相离两种情况
显然这些询问形成了一棵树
对询问建树
跑dsu

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
inline LL _max(LL x,LL y){return x>y?x:y;}
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(LL x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(LL x){write(x);puts("");}
struct LSnode{int y,p;}w[550005];
bool cmp(LSnode n1,LSnode n2){return n1.y<n2.y;}
int cal[550005],LS[550005],fac[550005],n,m,ln;
struct ask{int l,r,pa;}A[2100005];
bool Acmp(ask n1,ask n2){return (n1.l!=n2.l)?(n1.l<n2.l):(n1.r>n2.r);}
struct edge{int x,y,next;}a[2110000];int len,last[550005];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
LL aa[550005];
int sta[550005],tp;

LL tot[550005];
int son[550005];
void pre_tree_node(int x)
{
	tot[x]=A[x].r-A[x].l+1;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		pre_tree_node(y);
		tot[x]+=tot[y];
		if(tot[y]>tot[son[x]])son[x]=y;
	}
}
LL ans[550005],ggcc;
int sum[550005],gg[550005],tim;
void add(int u)
{
	if(gg[u]!=tim)gg[u]=tim,sum[u]=0;
	sum[u]++;ggcc=_max(ggcc,(LL)sum[u]*fac[u]);
}
void updata(int x)
{
	int y=son[x];
	if(!y)for(int i=A[x].l;i<=A[x].r;i++)add(cal[i]);
	else
	{
		for(int i=A[x].l;i<A[y].l;i++)add(cal[i]);
		for(int i=A[y].r+1;i<=A[x].r;i++)add(cal[i]);
	}
}
void dsu(int x)
{
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y==son[x])continue;//是重儿子 最后处理
		dsu(y);//否则直接计算答案 
		tim++;
	}
	if(son[x])dsu(son[x]);
	ggcc=ans[son[x]];
	updata(x);ans[x]=ggcc;
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++)w[i].y=read(),w[i].p=i;
	sort(w+1,w+1+n,cmp);
	int tt=0;
	for(int i=1;i<=n;i++)
	{
		if(w[i].y!=w[i-1].y)tt++;
		cal[w[i].p]=tt;fac[tt]=w[i].y;
	}
	for(int i=1;i<=m;i++)A[i].l=read(),A[i].r=read(),A[i].pa=i;
	A[++m].l=0;A[m].r=n+1;A[m].pa=m+1;
	sort(A+1,A+1+m,Acmp);
	sta[tp=1]=1;
	for(int i=2;i<=m;i++)
	{
		int x;
		while(tp)
		{
			x=sta[tp];
			if(A[x].r<A[i].l)tp--;
			else break;
		}
		ins(x,i);sta[++tp]=i;
	}
	pre_tree_node(1);
	dsu(1);
	for(int i=1;i<=m;i++)aa[A[i].pa]=ans[i];
	for(int i=1;i<m;i++)print(aa[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/82772747