题目传送门:https://www.luogu.org/problem/show?pid=1081
题目分析:这题的两个问其实是差不多的。第一问给出了X0,我们枚举起点s,就相当于变成了n个第二类询问 (s,X0)(1<=s<=n) 。于是现在原问题变成了:给出(s,x),如何快速求s开始往下走不超过x距离时,A,B各走的距离?然后用数据结构预处理出A,B到达每一个点之后会走哪个点,询问时倍增往后跳统计答案即可。调代码的时候注意A可能比B多走一步的情况,以及A,B走的距离均为0的情况。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100100;
const int maxl=22;
const long long M1=998244353;
const long long M2=1000000007;
const long long M3=1333333331;
typedef long long LL;
struct Tnode
{
int id,fix;
Tnode *lson,*rson;
} tree[maxn];
Tnode *Root=NULL;
int cur=-1;
int Next1[maxn];
int Next2[maxn];
int Next[maxn][maxl];
LL disA[maxn][maxl];
LL disB[maxn][maxl];
int a[maxn];
int n,m;
LL seed,X0;
LL sumA,sumB;
int Rand()
{
seed=(seed*M1+M2)%M3;
return (int)seed;
}
Tnode *New_node(int v)
{
cur++;
tree[cur].id=v;
tree[cur].fix=Rand();
tree[cur].lson=tree[cur].rson=NULL;
return tree+cur;
}
void Right_turn(Tnode *&P)
{
Tnode *W=P->lson;
P->lson=W->rson;
W->rson=P;
P=W;
}
void Left_turn(Tnode *&P)
{
Tnode *W=P->rson;
P->rson=W->lson;
W->lson=P;
P=W;
}
void Insert(Tnode *&P,int v)
{
if (!P) P=New_node(v);
else
if ( a[v]<a[ P->id ] )
{
Insert(P->lson,v);
if ( P->lson->fix < P->fix ) Right_turn(P);
}
else
{
Insert(P->rson,v);
if ( P->rson->fix < P->fix ) Left_turn(P);
}
}
int Get_prev(Tnode *P,int x,int v)
{
if (!P) return v;
if ( a[ P->id ]<x ) return Get_prev(P->rson,x,P->id);
return Get_prev(P->lson,x,v);
}
int Get_succ(Tnode *P,int x,int v)
{
if (!P) return v;
if ( x<a[ P->id ] ) return Get_succ(P->lson,x,P->id);
return Get_succ(P->rson,x,v);
}
int Abs(int x)
{
if (x>=0) return x;
return -x;
}
void Work(int s,int x)
{
sumA=0,sumB=0;
LL Left=x;
for (int j=maxl-1; j>=0; j--)
if ( Next[s][j] && disA[s][j]+disB[s][j]<=Left )
{
Left-=(disA[s][j]+disB[s][j]);
sumA+=disA[s][j];
sumB+=disB[s][j];
s=Next[s][j];
}
if ( Next2[s] && Abs(a[ Next2[s] ]-a[s])<=Left) sumA+=Abs(a[ Next2[s] ]-a[s]);
}
int main()
{
freopen("1081.in","r",stdin);
freopen("1081.out","w",stdout);
scanf("%d",&n);
seed=n;
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
for (int i=n; i>=1; i--)
{
Insert(Root,i);
int Prev=Get_prev(Root,a[i],0);
int Succ=Get_succ(Root,a[i],0);
if ( !Prev && !Succ ) continue;
if ( Prev && Succ )
if ( a[i]-a[Prev]<=a[Succ]-a[i] ) Succ=0;
else Prev=0;
if (Prev)
{
Next1[i]=Prev;
Prev=Get_prev(Root,a[Prev],0);
Succ=Get_succ(Root,a[i],0);
if ( !Prev && !Succ ) continue;
if ( Prev && Succ )
if ( a[i]-a[Prev]<=a[Succ]-a[i] ) Succ=0;
else Prev=0;
if (Prev) Next2[i]=Prev;
else Next2[i]=Succ;
}
else
{
Next1[i]=Succ;
Prev=Get_prev(Root,a[i],0);
Succ=Get_succ(Root,a[Succ],0);
if ( !Prev && !Succ ) continue;
if ( Prev && Succ )
if ( a[i]-a[Prev]<=a[Succ]-a[i] ) Succ=0;
else Prev=0;
if (Prev) Next2[i]=Prev;
else Next2[i]=Succ;
}
}
//for (int i=1; i<=n; i++) printf("%d %d\n",Next1[i],Next2[i]);
for (int i=1; i<=n; i++)
{
int mid=Next2[i];
Next[i][0]=Next1[mid];
if (Next[i][0])
{
disA[i][0]=Abs(a[mid]-a[i]);
disB[i][0]=Abs(a[ Next1[mid] ]-a[mid]);
}
}
for (int j=1; j<maxl; j++)
for (int i=1; i<=n; i++)
{
int mid=Next[i][j-1];
Next[i][j]=Next[mid][j-1];
if (Next[i][j])
{
disA[i][j]=disA[i][j-1]+disA[mid][j-1];
disB[i][j]=disB[i][j-1]+disB[mid][j-1];
}
}
//for (int i=1; i<=n; i++) printf("%d\n",Next[i][0]);
scanf("%lld",&X0);
int ans=0;
LL ansA=M1,ansB=-1;
for (int s=1; s<=n; s++)
{
Work(s,X0);
if ( sumA*ansB<ansA*sumB || ( sumA*ansB==ansA*sumB && a[s]>a[ans] ) )
{
if ( !sumB && ansB>0 ) ;
else
{
ansA=sumA;
ansB=sumB;
ans=s;
}
}
}
printf("%d\n",ans);
scanf("%d",&m);
for (int i=1; i<=m; i++)
{
int s,x;
scanf("%d%d",&s,&x);
Work(s,x);
printf("%lld %lld\n",sumA,sumB);
}
return 0;
}