倍增神题????
细节超级多···
其实思路很简单
对于每个点,A和B要开去的目的地是固定的,所以就想到了倍增
开三个倍增数组,t[i][j][k]代表k从i开车走2^j天走到的那个城市
f是A走的路程,g是B走的路程
0代表A,1代表B
对于预处理,一开始怎么想怎么是n^2的
后来自己yy了一下觉得每次sort一下也行嘛!
look!就像这样
sort(a+i+1,a+n+1,cmp);
int x=lower_bound(a+i+1,a+n+1,a[i])-a;
if(abs(a[x].h-a[i].h)>abs(a[x-1].h-a[i].h)) x--;
g[i][0][1]=abs(a[x].h-a[i].h); t[i][0][1]=x;
int y;
if(abs(a[x-1].h-a[i].h)<=abs(a[x+1].h-a[i].h)) y=x-1;
else y=x+1;
外层i从n-1循环到1
但是我发现这样子很容易会找到它自己,好像连样例2都过不了
于是想到了排序+二分
我觉得这个其实也能过,但写到一半放弃了觉得一定有更简单的方法
这时候问了一下小伙伴他们看的是哪个题解(划掉
然后学习到了一种高端操作(其实很简单
那就是双向链表!
sort(a+1,a+n+1);//get到了双向链表大法!
for(int i=1;i<=n;i++) a[i].l=i-1,a[i].r=i+1,pos[a[i].id]=i;
a[1].l=a[n].r=0;
for(int i=1;i<=n;i++){
aim=pos[i]; l=a[aim].l; r=a[aim].r;
if(quq()) nb[i]=a[l].id,na[i]=qvq(a[l].l,r);//左边更近
else nb[i]=a[r].id,na[i]=qvq(l,a[r].r);//右边更近
if(l) a[l].r=r;//
if(r) a[r].l=l;//delete
}
它可以很方便快捷地解决掉这种有限制性的排序问题
因为每次只能从小往大走,所以找完一个小的就把它从链表删掉
这样找后面的时候一定不会找到它
又因为事先排好了序,所以只要看左边和右边哪个更小就好啦!
是不是特别简单!!!
好了,预处理解决完,就可以开始求倍增数组了
和lca的套路十分相似,也是根据2次幂,但是要注意1的时候和>1的时候不一样
所以要单独考虑一下
求好了倍增数组,这道题就做完了80%
然而我却wa在了最后求答案的地方
因为倍增的题只做过lca···
所以十分naive地以为只要贪心地找到第一个比x0小的就好了
啊我怎么那么傻啊喂,有可能还到不了x0啊
给你们看看我的傻代码:
for(int i=1;i<=n;i++)
for(int j=17;j>=0;j--){
if(t[i][j][0]>0 && f[i][j][0]+g[i][j][0]<=1LL*x0 && g[i][j][0]>0)
if(ans-(double)f[i][j][0]/g[i][j][0]>eps) ans=(double)f[i][j][0]/g[i][j][0];
else if(ans==(double)f[i][j][0]/g[i][j][0]) aim=(a[aim].h>a[i].h)?aim:i;
else if(f[i][j][0]+g[i][j][0]<=1LL*x0 && g[i][j][0]==0 && aim==0) aim=i;
}
还有这个:
s=rd(); x0=rd();
for(int i=17;i>=0;i--){
if(t[s][i][0]>0 && f[s][i][0]+g[s][i][0]<=1LL*x0){
printf("%lld %lld\n",f[s][i][0],g[s][i][0]);
flg=true; break;
}
}
这错误思想简直经典
其实正确写法应该是这样:
inline void solve(int s,int x){//哦原来找是这么个找法,跟求lca差不多的样子
la=lb=0; int k=0;
for(int j=lg;j>=0;j--)
if(t[s][j][k] && f[s][j][k]+g[s][j][k]<=x){
x-=f[s][j][k]+g[s][j][k];
la+=f[s][j][k], lb+=g[s][j][k];
if(j==0) k^=1;
s=t[s][j][k];
}
}
看来基本上所有的倍增都是这么一个模板了0.0
说完这些整个题就已经出来了啊喂
这个看似复杂的题就被我们一步一步地解决掉了
其实所有很复杂的题都可以化成简单的小问题解决的吧qvq
接下来看一下我的AC代码!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100005
#define LL long long
using namespace std;
int n,m,s,x0,t[maxn][20][2],lg;//0->A 1->B
int pos[maxn],aim,l,r,na[maxn],nb[maxn];
LL g[maxn][20][2],f[maxn][20][2],ans1,ans2,la,lb;//f->A g->B
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct qwq{
int h,id,l,r;
bool operator < (const qwq &x) const {
return h<x.h;
}
}a[maxn];
inline bool cmp1(qwq x,qwq y) {return x.id<y.id;}
inline bool quq(){
if(!l) return false; if(!r) return true;
return a[aim].h-a[l].h<=a[r].h-a[aim].h;
}
inline int qvq(int x,int y){
if(!x) return a[y].id;
if(!y) return a[x].id;
if(a[aim].h-a[x].h<=a[y].h-a[aim].h) return a[x].id;
return a[y].id;
}
inline void drive(){
sort(a+1,a+n+1);//get到了双向链表大法!
for(int i=1;i<=n;i++) a[i].l=i-1,a[i].r=i+1,pos[a[i].id]=i;
a[1].l=a[n].r=0;
for(int i=1;i<=n;i++){
aim=pos[i]; l=a[aim].l; r=a[aim].r;
if(quq()) nb[i]=a[l].id,na[i]=qvq(a[l].l,r);//左边更近
else nb[i]=a[r].id,na[i]=qvq(l,a[r].r);//右边更近
if(l) a[l].r=r;//
if(r) a[r].l=l;//delete
}
sort(a+1,a+n+1,cmp1);
for(int i=n-1;i;i--){
int x=nb[i],y=na[i];
f[i][0][0]=abs(a[y].h-a[i].h),t[i][0][0]=y;
g[i][0][1]=abs(a[x].h-a[i].h),t[i][0][1]=x;
if(i==n-1){
f[i][0][0]=0;
}
t[i][1][0]=t[y][0][1]; f[i][1][0]=f[i][0][0]; f[i][1][1]=f[x][0][0];
t[i][1][1]=t[x][0][0]; g[i][1][0]=g[y][0][1]; g[i][1][1]=g[i][0][1];
}
for(int j=2;j<=lg;j++)
for(int i=1;i<=n;i++)
for(int k=0;k<=1;k++){
if(t[i][j-1][k]) t[i][j][k]=t[t[i][j-1][k]][j-1][k];
if(t[i][j-1][k])
f[i][j][k]=f[t[i][j-1][k]][j-1][k]+f[i][j-1][k],
g[i][j][k]=g[t[i][j-1][k]][j-1][k]+g[i][j-1][k];
}
}
inline void solve(int s,int x){//哦原来找是这么个找法,跟求lca差不多的样子
la=lb=0; int k=0;
for(int j=lg;j>=0;j--)
if(t[s][j][k] && f[s][j][k]+g[s][j][k]<=x){
x-=f[s][j][k]+g[s][j][k];
la+=f[s][j][k], lb+=g[s][j][k];
if(j==0) k^=1;
s=t[s][j][k];
}
}
int main(){
n=rd();
for(int i=1;i<=n;i++) a[i].h=rd(),a[i].id=i;
lg=log(n*1.0)/log(2.0);
drive();
x0=rd();
aim=0; ans1=1; ans2=0;
for(int i=1;i<=n;i++){
solve(i,x0);
if(!lb) la=1;
if(la*ans2<lb*ans1 || (la*ans2==lb*ans1 && a[i].h>a[aim].h))
ans1=la,ans2=lb,aim=i;
}
printf("%d\n",aim);
m=rd();
while(m--){
flg=false;
s=rd(); x0=rd();
solve(s,x0);
printf("%lld %lld\n",la,lb);
}
return 0;
}