题解
这题相比起历史研究,有一个十分重要的条件
Ai<Aj<Bi<=Bj
这句话什么意思呢?
就是说,任意两个操作,只有相离和内含两个关系
这启示我们,如果我们把最近一个包含关系连边的话,他是一棵树
比如说,三个询问(1,3)(1,2)(1,1)
我们可以这么连边(1,3)->(1,2)->(1,1)
这样的话就是一棵树了,是吧
然后我们就是在树上面考虑这个问题
其实就是树上每个点有个L,R
然后问我们这一段怎么搞
当然他有很多孩子
这种问题,很明显,是可以将孩子并到父亲那里去的
那怎么搞呢?
Dsu on tree!
时间复杂度和做法在上面都有了,我就不再赘述了
于是,就可以在
nlogn
的复杂度里面优秀地解决这个题了!
至于怎么建树,一开始想的是按长度排序,然后用一个线段树维护这个区间要连向谁
但后来采取了一个单调栈的方法
就是,我们按L为第一关键字,R为第二关键字,第一个升序,第二个降序
然后搞一搞就好了。。具体看代码。。
当然,其实如果你写线段树合并的话,我不知道可不可以啊。。反正没有Dsu on tree好写吧。。
最后这题要卡常。。我优化了好一会,才10s卡过。。
数据生成器(你可以先生成一堆一定是包含的,然后再再里面随机):
#include<bits/stdc++.h>
using namespace std;
int L[1000000],R[1000000];
int tot=0;
int n=550000,t=400000;
void solve (int l,int r)
{
int op=rand()%10;
if (op==0&&tot<t) L[++tot]=l,R[tot]=r;
if (l==r) return ;
int mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
}
int main()
{
srand(time(0));
solve(1,n);
printf("%d %d\n",n,tot);
for (int u=1;u<=n;u++) printf("%d ",rand()*rand()%10000+10000);
printf("\n");
for (int u=1;u<=tot;u++)
printf("%d %d\n",L[u],R[u]);
}
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
typedef long long LL;
const int N=600005*2;
int n,q;
int a[N];
struct qq
{
int l,r,id;
}s[N];
LL ans[N];
struct qt
{
int x,y,last;
}e[N];int num,last[N];
bool cmp (qq a,qq b) {return a.l==b.l?a.r>b.r:a.l<b.l;}
bool cmpi (qq a,qq b) {return a.id<b.id;}
void init (int x,int y)
{
num++;
e[num].x=x;e[num].y=y;
e[num].last=last[x];
last[x]=num;
}
stack<int> S;
int son[N],tot[N];
void dfs1 (int x)
{
tot[x]=(s[x].r-s[x].l+1);
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
dfs1(y);
tot[x]=tot[x]+tot[y];
if (tot[son[x]]<tot[y]) son[x]=y;
}
}
int tp;
void BT ()
{
num=0;memset(last,-1,sizeof(last));
s[++q].l=1;s[q].r=n;s[q].id=q;
sort(s+1,s+1+q,cmp);
S.push(1);
for (int u=2;u<=q;u++)
{
int x;
while (!S.empty())
{
x=S.top();
if (s[x].r<s[u].l) S.pop();
else break;
}
init(s[x].id,s[u].id);
S.push(u);
}
tp=s[1].id;
sort(s+1,s+1+q,cmpi);
dfs1(tp);
}
int h[N];//这个东西出现了多少次
int cnt;
int cnt1[N];//清0标记
LL lalal;//当前的答案是什么
int b[N];//这个数其实是什么
void add (int x)
{
if (cnt1[x]!=cnt)//如果这个要请0
{
h[x]=0;
cnt1[x]=cnt;
}
h[x]++;
LL t=(LL)h[x]*b[x];
if (t>lalal) lalal=t;
}
void change (int x)
{
int y=son[x];
if (y==0)
{
for (int u=s[x].l;u<=s[x].r;u++)
add(a[u]);
}
else
{
for (int u=s[x].l;u<s[y].l;u++)
add(a[u]);
for (int u=s[y].r+1;u<=s[x].r;u++)
add(a[u]);
}
}
void dfs (int x)//哪一个点,他是不是重儿子
{
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==son[x]) continue;
dfs(y);
cnt++;
}
if (son[x]!=0) dfs(son[x]);
lalal=ans[son[x]];
change(x);
ans[x]=lalal;
}
void LSH ()
{
sort(b+1,b+1+n);
int tot=1;
for (int u=2;u<=n;u++)
if (b[u]!=b[tot])
b[++tot]=b[u];
for (int u=1;u<=n;u++)
{
int l=1,r=tot;
while (l<=r)
{
int mid=(l+r)>>1;
if (b[mid]==a[u]) {a[u]=mid;break;}
else if (b[mid]>a[u]) r=mid-1;
else if (b[mid]<a[u]) l=mid+1;
}
}
}
inline int read()
{
int x=0,f=1;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;
}
int main()
{
n=read();q=read();
int Q=q;
for (int u=1;u<=n;u++)
{
a[u]=read();
b[u]=a[u];
}
LSH();
for (int u=1;u<=q;u++)
{
s[u].l=read();s[u].r=read();
s[u].id=u;
}
BT();
dfs(tp);
for (int u=1;u<=Q;u++) printf("%lld\n",ans[u]);
return 0;
}