NOIP2012 提高组 Day 1

时间:2021-06-17 15:27:13

期望得分:100+100+70=270

实际得分:100+50+70=220

T2 没有底

最后剩余时间来不及打高精、思路出现错误

T1 Vigenère 密码

题目描述

16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密码。Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为南军所广泛使用。在密码学中,我们称需要加密的信息为明文,用 M 表示;称加密后的信息为密文,用C 表示;而密钥是一种参数,是将明文转换为密文或将密文转换为明文的算法中输入的数据,记为 k。 在 Vigenère 密码中,密钥 k 是一个字母串,k=k1k2…kn。当明文 M=m1m2…mn时,得到的密文 C=c1c2…cn,其中 ci=mi®ki,运算®的规则如下表所示:

NOIP2012 提高组 Day 1

Vigenère 加密在操作时需要注意:

  1. ®运算忽略参与运算的字母的大小写,并保持字母在明文 M 中的大小写形式;

  2. 当明文 M 的长度大于密钥 k 的长度时,将密钥 k 重复使用。

例如,明文 M=Helloworld,密钥 k=abc 时,密文 C=Hfnlpyosnd。

NOIP2012 提高组 Day 1

输入输出格式

输入格式:

输入共 2 行。

第一行为一个字符串,表示密钥 k,长度不超过 100,其中仅包含大小写字母。第二行为一个字符串,表示经加密后的密文,长度不超过 1000,其中仅包含大小写字母。

输出格式:

输出共 1 行,一个字符串,表示输入密钥和密文所对应的明文。

输入输出样例

输入样例#1:
CompleteVictory
Yvqgpxaimmklongnzfwpvxmniytm
输出样例#1:
Wherethereisawillthereisaway 

说明

【数据说明】

对于 100%的数据,输入的密钥的长度不超过 100,输入的密文的长度不超过 1000,且都仅包含英文字母。

定义字符ch的ASCLL码为 a(ch)

则a(明文)= [a(密文)+26-a(密匙)]%26+1

#include<cstdio>
#include<cstring>
using namespace std;
char a[],b[];
void change(int i)
{
if(a[i]<='Z') return;
a[i]-=;
}
int main()
{
/*freopen("vigenere.in","r",stdin);
freopen("vigenere.out","w",stdout);*/
scanf("%s%s",a,b);
int lena=strlen(a);
for(int i=;i<lena;i++) change(i);
int lenb=strlen(b),j=;
int x,y,z;
for(int i=;i<lenb;i++)
{
if(j==lena) j=;
x=a[j]-'A'+;
y=b[i]>= ? b[i]-'a'+ : b[i]-'A'+;
z=(y+-x)%+;
z=b[i]>= ? z+-:z+-;
putchar(z);
j++;
}
}

T2 国王游戏

题目描述

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

输入输出格式

输入格式:

第一行包含一个整数 n,表示大臣的人数。

第二行包含两个整数 a和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出格式:

输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

输入输出样例

输入样例#1:
3
1 1
2 3
7 4
4 6
输出样例#1:
2

说明

【输入输出样例说明】

按 1、2、3 号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 1、3、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 2、1、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 2、3、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;

按 3、1、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 3、2、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9。

因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2。

【数据范围】

对于 20%的数据,有 1≤ n≤ 10,0 < a、b < 8;

对于 40%的数据,有 1≤ n≤20,0 < a、b < 8;

对于 60%的数据,有 1≤ n≤100;

对于 60%的数据,保证答案不超过 109;

对于 100%的数据,有 1 ≤ n ≤1,000,0 < a、b < 10000。

题意:改变序列中大臣的顺序

使最大的 ( ∏ left[i] ) / right[j]  i<j  最小

分析得交换相邻两个大臣i和j的位置,对i前面前面的结果没有影响,对j后面的结果也没有影响

只会影响i和j的结果

定义m为大臣获得的金币数,s= ∏ left[k]   1<=k<i

A、 i 在 j 前面

m(i)= s / right[i]  ①     m(j)= s * left[i] / right[j]  ②

B、j 在 i 前面

m(i)= s * left[j] / right[i]  ③     m(j)= s / right[j]  ④

① <③    ④ < ②

题意要从A中选最大的m,设其为a,B中选最大的m,设其为b。ans=min(a,b)

若  ① > ② ,③ > ④ ,则a=①,b=③ ∵①<③  ∴方案A更优 由①②得,left[i] * right[i] < right[j],i在前

若  ① > ② ,③ < ④ ,那么 ③ > ① > ② > ④ > ③ 不存在

若  ① < ② ,③ > ④ , 那么a=② ,b=③

如果 a<b,此时方案A更优,由②③得, left[i]* right[i]< left[j]* right[j], i在前

如果 a>b,此时方案B更优,由②③的,left[j]* right[j]<left[i]* right[i] , j在前

若 ① < ② ,③ < ④,那么a=②,b=④  ∵ ④ < ②  ∴方案B更优 由③④得,left[j] * right[j] < right[i] ,j在前

所以每次都是left*right小的在前

所以解法:按左右手乘积从小到大排序,然后一个一个算,取最大的

再就是要高精度

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,aa,bb,a[],b[],c[],ans[],tmp[],d,kk[];
int x;
struct node
{
int l,r;
}e[];
bool cmp3(node p,node q)
{
return p.l*p.r<q.l*q.r;
}
void mul()
{
memset(c,,sizeof(c));
for(int i=;i<=a[];i++)
{
for(int j=;j<=b[];j++)
{
x=c[i+j-]+a[i]*b[j];
c[i+j-]=x%;
c[i+j]+=x/;
}
}
c[]=a[]+b[];
while(!c[c[]]&&c[]>) c[]--;
for(int i=;i<=c[];i++) a[i]=c[i];
a[]=c[];
}
void divide()
{
x=;
for(int i=;i<=a[];i++)
{
x=x*+a[i];
c[i]=x/d;
x=x%d;
}
int i;
for(i=;i<a[];i++)
if(c[i]) break;
tmp[]=;
for(int j=i;j<=a[];j++) tmp[++tmp[]]=c[j];
}
void change(int i)
{
b[]=;
while(i)
{
b[++b[]]=i%;
i/=;
}
}
void cmp()
{
if(tmp[]>ans[])
{
for(int i=;i<=tmp[];i++)
ans[i]=tmp[i];
}
else if(tmp[]==ans[])
{
for(int i=;i<=tmp[];i++)
{
if(tmp[i]>ans[i])
{
for(int j=i;j<=tmp[];j++) ans[j]=tmp[j];
}
else if(tmp[i]<ans[i]) return;
}
}
}
void reverse(int *p)
{
kk[]=;
for(int i=p[];i>=;i--) kk[++kk[]]=p[i];
for(int i=;i<=kk[];i++) p[i]=kk[i];
}
int main()
{
freopen("kinggame.in","r",stdin);
freopen("kinggame.out","w",stdout);
scanf("%d",&n);
scanf("%d%d",&aa,&bb);
for(int i=;i<=n;i++) scanf("%d%d",&e[i].l,&e[i].r);
sort(e+,e+n+,cmp3);
int t=aa/e[].r;
while(t)
{
ans[++ans[]]=t%;
t/=;
}
reverse(ans);
while(aa)
{
a[++a[]]=aa%;
aa/=;
}
reverse(a);
for(int i=;i<n;i++)
{
change(e[i].l);
reverse(a);
mul();
reverse(a);
d=e[i+].r;
divide();
cmp();
}
for(int i=;i<=ans[];i++) printf("%d",ans[i]);
}

自己做的时候,没推出结论,四种贪心按a排,按b排,按a+b排,按a*b排取最优

没写高精度拿了50,因为误认为最大的一定是最后一个

#include<cstdio>
#include<algorithm>
using namespace std;
int n,a,b;
long long ans=1e17,t;
double tt;
bool ok;
struct node
{
int l,r;
}tmp[];
bool cmp1(node p,node q)
{
return p.r<q.r;
}
bool cmp2(node p,node q)
{
return p.l<q.l;
}
bool cmp3(node p,node q)
{
return p.l*p.r<q.l*q.r;
}
bool cmp4(node p,node q)
{
return p.l+p.r<q.l+q.r;
}
int main()
{
freopen("kinggame.in","r",stdin);
freopen("kinggame.out","w",stdout);
scanf("%d",&n);
scanf("%d%d",&a,&b);
for(int i=;i<=n;i++) scanf("%d%d",&tmp[i].l,&tmp[i].r); t=a;tt=a;ok=false;
sort(tmp+,tmp+n+,cmp1);
for(int i=;i<n;i++)
{
if(tt*tmp[i].l>1e9) { ok=true; break;}
tt*=tmp[i].l;t*=tmp[i].l;
}
if(!ok) ans=t/tmp[n].r; t=a;tt=a;ok=false;
sort(tmp+,tmp+n+,cmp2);
for(int i=;i<n;i++)
{
if(tt*tmp[i].l>1e9) { ok=true; break;}
tt*=tmp[i].l;t*=tmp[i].l;
}
if(!ok) ans=min(ans,t/tmp[n].r); t=a;tt=a;ok=false;
sort(tmp+,tmp+n+,cmp3);
for(int i=;i<n;i++)
{
if(tt*tmp[i].l>1e9) { ok=true; break;}
tt*=tmp[i].l;t*=tmp[i].l;
}
if(!ok) ans=min(ans,t/tmp[n].r); t=a;tt=a;ok=false;
sort(tmp+,tmp+n+,cmp4);
for(int i=;i<n;i++)
{
if(tt*tmp[i].l>1e9) { ok=true; break;}
tt*=tmp[i].l;t*=tmp[i].l;
}
if(!ok) ans=min(ans,t/tmp[n].r); printf("%lld",ans);
}

高精度把数组改成了字符数组,然后就挂了

挂了的代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
string aa,bb,a,b;
string ans(,'\0');
int x,y,d,len,len2;
struct node
{
int l,r;
}e[];
bool cmp3(node p,node q)
{
return p.l*p.r<q.l*q.r;
}
void mul()
{
string e(,'\0');
len=a.size();
len2=b.size();
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
for(int i=;i<len;i++)
for(int j=;j<len2;j++)
{
x=e[i+j]-''+(a[i]-'')*(b[i]-'');
e[i+j]=char(x%+);
e[i+j+]=char(e[i+j+]-''+x/+);
}
a=e;
cout<<a<<endl;
}
void divide()
{
len=a.size();
string e(,'\0');
for(int i=;i<len;i++)
{
x=x*+a[i]-'';
y=x/d;
e[i]=char(y+);
x=x%d;
}
int i;
for(i=;i<len;i++)
if(e[i]!='') break;
string tmp(,'\0');
for(int j=i;j<len;j++) tmp+=e[i];
if(ans<tmp)
ans=tmp;
if(ans[]!='\0')
cout<<ans<<endl;
}
void change(int i)
{
b.empty();
while(i)
{
b+=char(i+);
i/=;
}
}
int main()
{
scanf("%d",&n);
cin>>aa>>bb;
for(int i=;i<=n;i++) scanf("%d%d",&e[i].l,&e[i].r);
sort(e+,e+n+,cmp3);
d=e[].r;
a=aa;
cout<<a<<endl;
divide();
for(int i=;i<n;i++)
{
change(e[i].l);
cout<<b<<endl;
mul();
d=e[i+].r;
reverse(a.begin(),a.end());
divide();
}
cout<<ans;
}

T3 开车旅行

题目描述

小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的

城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为

Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即

d[i,j] = |Hi− Hj|。 旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。他们计划

选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。小 A 和小 B

的驾驶风格不同,小 B 总是沿着前进方向选择一个最近的城市作为目的地,而小 A 总是沿

着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离

相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的

城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。

在启程之前,小 A 想知道两个问题:

1.对于一个给定的 X=X0,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶

的路程总数的比值最小(如果小 B 的行驶路程为 0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比

值都最小,则输出海拔最高的那个城市。

  1. 对任意给定的 X=Xi和出发城市 Si,小 A 开车行驶的路程总数以及小 B 行驶的路程

总数。

输入输出格式

输入格式:

第一行包含一个整数 N,表示城市的数目。

第二行有 N 个整数,每两个整数之间用一个空格隔开,依次表示城市 1 到城市 N 的海

拔高度,即 H1,H2,……,Hn,且每个 Hi都是不同的。

第三行包含一个整数 X0。

第四行为一个整数 M,表示给定 M 组 Si和 Xi。

接下来的 M 行,每行包含 2 个整数 Si和 Xi,表示从城市 Si出发,最多行驶 Xi公里。

输出格式:

输出共 M+1 行。

第一行包含一个整数 S0,表示对于给定的 X0,从编号为 S0的城市出发,小 A 开车行驶

的路程总数与小 B 行驶的路程总数的比值最小。

接下来的 M 行,每行包含 2 个整数,之间用一个空格隔开,依次表示在给定的 Si和

Xi下小 A 行驶的里程总数和小 B 行驶的里程总数。

输入输出样例

输入样例#1:
drive1
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3 drive2
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7
输出样例#1:
drive1
1
1 1
2 0
0 0
0 0 drive2
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0

说明

【输入输出样例 1 说明】

NOIP2012 提高组 Day 1

各个城市的海拔高度以及两个城市间的距离如上图所示。

如果从城市 1 出发,可以到达的城市为 2,3,4,这几个城市与城市 1 的距离分别为 1,1,2,

但是由于城市 3 的海拔高度低于城市 2,所以我们认为城市 3 离城市 1 最近,城市 2 离城市

1 第二近,所以小 A 会走到城市 2。到达城市 2 后,前面可以到达的城市为 3,4,这两个城

市与城市 2 的距离分别为 2,1,所以城市 4 离城市 2 最近,因此小 B 会走到城市 4。到达城

市 4 后,前面已没有可到达的城市,所以旅行结束。

如果从城市 2 出发,可以到达的城市为 3,4,这两个城市与城市 2 的距离分别为 2,1,由

于城市 3 离城市 2 第二近,所以小 A 会走到城市 3。到达城市 3 后,前面尚未旅行的城市为

4,所以城市 4 离城市 3 最近,但是如果要到达城市 4,则总路程为 2+3=5>3,所以小 B 会

直接在城市 3 结束旅行。

如果从城市 3 出发,可以到达的城市为 4,由于没有离城市 3 第二近的城市,因此旅行

还未开始就结束了。

如果从城市 4 出发,没有可以到达的城市,因此旅行还未开始就结束了。

【输入输出样例 2 说明】

当 X=7 时,

如果从城市 1 出发,则路线为 1 -> 2 -> 3 -> 8 -> 9,小 A 走的距离为 1+2=3,小 B 走的

距离为 1+1=2。(在城市 1 时,距离小 A 最近的城市是 2 和 6,但是城市 2 的海拔更高,视

为与城市 1 第二近的城市,所以小 A 最终选择城市 2;走到 9 后,小 A 只有城市 10 可以走,

没有第 2 选择可以选,所以没法做出选择,结束旅行)

如果从城市 2 出发,则路线为 2 -> 6 -> 7 ,小 A 和小 B 走的距离分别为 2,4。

如果从城市 3 出发,则路线为 3 -> 8 -> 9,小 A 和小 B 走的距离分别为 2,1。

如果从城市 4 出发,则路线为 4 -> 6 -> 7,小 A 和小 B 走的距离分别为 2,4。

如果从城市 5 出发,则路线为 5 -> 7 -> 8 ,小 A 和小 B 走的距离分别为 5,1。

如果从城市 6 出发,则路线为 6 -> 8 -> 9,小 A 和小 B 走的距离分别为 5,1。

如果从城市 7 出发,则路线为 7 -> 9 -> 10,小 A 和小 B 走的距离分别为 2,1。

如果从城市 8 出发,则路线为 8 -> 10,小 A 和小 B 走的距离分别为 2,0。

全国信息学奥林匹克联赛(NOIP2012)复赛

提高组 day1

第 7 页 共 7 页

如果从城市 9 出发,则路线为 9,小 A 和小 B 走的距离分别为 0,0(旅行一开始就结

束了)。

如果从城市 10 出发,则路线为 10,小 A 和小 B 走的距离分别为 0,0。

从城市 2 或者城市 4 出发小 A 行驶的路程总数与小 B 行驶的路程总数的比值都最小,

但是城市 2 的海拔更高,所以输出第一行为 2。

【数据范围】

对于 30%的数据,有 1≤N≤20,1≤M≤20;

对于 40%的数据,有 1≤N≤100,1≤M≤100;

对于 50%的数据,有 1≤N≤100,1≤M≤1,000;

对于 70%的数据,有 1≤N≤1,000,1≤M≤10,000;

对于100%的数据,有1≤N≤100,000,1≤M≤10,000,-1,000,000,000≤Hi≤1,000,000,000,

0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,数据保证 Hi互不相同。

一开始想法:

预处理距离每个城市最近的城市、次近的城市

一步一步模拟A、B的开车路线

时间复杂度:O(n²)

时间主要浪费在一步一步模拟A、B的开车路线

想想LCA是如何优化的——倍增

若能够预处理出f[i][j]表示从i开始,a、b各开2^j到达的点

同时记录行驶的距离dis[i][j],表示从i开始到达f[i][j]行驶的距离

然后对于每一个询问就可以在log(n)内回答

这里是a开一次b再开一次作为1次,所以log(n)算完之后

还要另外判断a是否还能在开一次

因为还要a、b各自行驶的距离

所以还要预处理fa[i][j],fb[i][j],adis[i][j],bdis[i][j]

含义与f,dis相同

然后仿照倍增求LCA预处理的思路,预处理这些数组

现在就剩最后一个问题了,预处理与每个城市相隔最近、次近的城市

显然不能n²扫一遍

怎么办?splay可以搞

我用的双向链表

先对所有的城市按高度排序,然后最近的和次近的只会在

前一个,前一个的前一个,后一个,后一个的后一个 中

还有一点,他只能往东跑

所以从最西边的城市开始,没处理完一个城市就把它删掉

双向链表可以方便的解决

AC代码:

#include<cmath>
#include<cstdio>
#include<algorithm>
#define N 100011
#define eps 1e-8
using namespace std;
int n,h[N],x0,m,s[N],xi[N],tmp;
int rankk[N],pos,x,y,xx,yy,p;
long long hx,hxx,hy,hyy;
int f[N][],fa[N][],fb[N][];
long long dis[N][];
long long adis[N][],bdis[N][];
struct node
{
int id,h;
int l,r;
}e[N];
bool cmp(node p,node q)
{
return p.h<q.h;
}
void init()
{
scanf("%d",&n);
for(int i=;i<=n;i++) scanf("%d",&e[i].h),e[i].id=i;
scanf("%d%d",&x0,&m);
for(int i=;i<=m;i++) scanf("%d%d",&s[i],&xi[i]);
}
void pre_first_step()
{
sort(e+,e+n+,cmp);
for(int i=;i<=n;i++) e[i].l=i-,e[i].r=i+;
e[n].r=; e[].h=-;
for(int i=;i<=n;i++) rankk[e[i].id]=i;
for(int i=;i<n;i++)
{
e[].l=e[].r=;pos=rankk[i];tmp=e[pos].h;
x=e[e[pos].l].id; xx=e[e[rankk[x]].l].id;
y=e[e[pos].r].id; yy=e[e[rankk[y]].r].id;
if(x) hx=tmp-e[rankk[x]].h;
else hx=0x7fffffff;
if(xx) hxx=tmp-e[rankk[xx]].h;
else hxx=0x7fffffff;
if(y) hy=e[rankk[y]].h-tmp;
else hy=0x7fffffff;
if(yy) hyy=abs(e[rankk[yy]].h-tmp);
else hyy=0x7fffffff;
if(hx==hy)
{
fb[i][]=x,fa[i][]=y,bdis[i][]=x?hx:,adis[i][]=y?hy:;
}
else if(hx<hy)
{
fb[i][]=x;bdis[i][]=x?hx:;
if(hxx<=hy) fa[i][]=xx,adis[i][]=xx?hxx:;
else fa[i][]=y,adis[i][]=y?hy:;
}
else
{
fb[i][]=y;bdis[i][]=y?hy:;
if(hyy<hx) fa[i][]=yy,adis[i][]=yy?hyy:;
else fa[i][]=x,adis[i][]=x?hx:;
}
e[e[pos].l].r=e[pos].r;
e[e[pos].r].l=e[pos].l;
}
}
void pre_doubly()
{
p=int(log(n)/log()+);
for(int i=;i<=p;i++)
for(int j=;j<=n;j++)
{
fa[j][i]=fa[fb[fa[j][i-]][]][i-];
fb[j][i]=fb[fa[fb[j][i-]][]][i-];
adis[j][i]=fa[j][i]?adis[j][i-]+adis[fb[fa[j][i-]][]][i-]:;
bdis[j][i]=fb[j][i]?bdis[j][i-]+bdis[fa[fb[j][i-]][]][i-]:;
}
for(int i=;i<=n;i++)
{
f[i][]=fb[fa[i][]][];
dis[i][]=f[i][]?adis[i][]+bdis[fa[i][]][]:;
}
for(int j=;j<=p;j++)
for(int i=;i<n;i++)
{
f[i][j]=f[f[i][j-]][j-];
dis[i][j]=f[i][j]?dis[i][j-]+dis[f[i][j-]][j-]:;
}
}
void solve()
{
long long sum,suma,sumb;
double maxn=1e9,tmp;
int ans;
int k;
for(int i=;i<n;i++)
{
k=i;
suma=sumb=sum=;
for(int j=p;j>=;j--)
{
if(dis[k][j]&&sum+dis[k][j]<=x0)
{
sum+=dis[k][j];
suma+=adis[k][j];
sumb+=bdis[fa[k][]][j];
k=f[k][j];
}
}
if(adis[k][]&&sum+adis[k][]<=x0)
{
sum+=adis[k][];
suma+=adis[k][];
}
if(!sumb) continue;
tmp=(double)suma/sumb;
if(maxn>tmp)
{
maxn=tmp;
ans=i;
}
}
printf("%d\n",ans);
for(int i=;i<=m;i++)
{
k=s[i];sum=suma=sumb=;
for(int j=p;j>=;j--)
{
if(dis[k][j]&&sum+dis[k][j]<=xi[i])
{
sum+=dis[k][j];
suma+=adis[k][j];
sumb+=bdis[fa[k][]][j];
k=f[k][j];
}
}
if(adis[k][]&&sum+adis[k][]<=xi[i])
{
sum+=adis[k][];
suma+=adis[k][];
}
printf("%lld ",suma);
printf("%lld\n",sumb);
}
}
int main()
{ init();
pre_first_step();
pre_doubly();
solve();
}

2大错误:

1、C++自带库函数log是以自然对数为底的,所以要求log2(n),可用

p=int(log(n)/log(2)+1);

2、fa[j][i]=fa[fb[fa[j][i-1]][0]][i-1];

不是fa[j][i]=fa[fb[fa[j][i-1]][i-1]][i-1];

70分TLE 暴力一步一步模拟代码:

#include<cmath>
#include<cstdio>
#include<algorithm>
#define N 100011
#define eps 1e-8
using namespace std;
int n,h[N],x0,m,s[N],x[N];
int first[N],second[N];
long long suma,sumb,dis;
long double bz=1e9,tmp=1e9;
int ans=,p,now;
int main()
{
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++) scanf("%d",&h[i]);
h[ans]=2e9+;
scanf("%d%d",&x0,&m);
for(int i=;i<=m;i++) scanf("%d%d",&s[i],&x[i]);
int k1,k2,minn1,minn2;
for(int i=;i<n;i++)
{
minn1=minn2=2e9+;
k1=k2=;
for(int j=i+;j<=n;j++)
if(abs(h[i]-h[j])<minn1)
{
minn2=minn1;
k2=k1;
minn1=abs(h[i]-h[j]);
k1=j;
}
else if(abs(h[i]-h[j])==minn1)
{
if(h[j]<h[k1])
{
minn2=minn1;
k2=k1;
k1=j;
}
else
{
minn2=minn1;
k2=j;
}
}
else if(abs(h[i]-h[j])<minn2)
{
minn2=abs(h[i]-h[j]);
k2=j;
}
else if(abs(h[i]-h[j])==minn2)
{
if(h[j]<h[k2]) k2=j;
}
first[i]=k1;second[i]=k2;
}
for(int i=;i<n;i++)
{
suma=sumb=dis=;
bz=1e9;
now=i;
for(int j=;;j++)
if(j&)
{
if(second[now]==||dis+abs(h[second[now]]-h[now])>x0)
{
if(!sumb)
{
bz=1e9;
break;
}
if(bz>(long double)suma/sumb)
{
bz=(double)suma/sumb;
ans=i;
}
else if((long double)suma/sumb-bz<=eps)
{
bz=(long double)suma/sumb;
ans= h[i]<h[ans] ? i :ans;
}
break;
}
else
{
suma+=abs(h[second[now]]-h[now]);
dis+=abs(h[second[now]]-h[now]);
now=second[now];
}
}
else
{
if(first[now]==||dis+abs(h[first[now]]-h[now])>x0)
{
if(!sumb)
{
bz=1e9;
break;
}
if(bz>(long double)suma/sumb)
{
bz=(long double)suma/sumb;
ans=i;
}
else if((long double)suma/sumb-bz<=eps)
{
bz=(long double)suma/sumb;
ans= h[i]<h[ans] ? i :ans;
}
break;
}
else
{
sumb+=abs(h[first[now]]-h[now]);
dis+=abs(h[first[now]]-h[now]);
now=first[now];
}
}
if(tmp>bz)
{
tmp=bz;
p=ans;
}
}
printf("%d\n",p);
for(int i=;i<=m;i++)
{
suma=sumb=dis=;
now=s[i];
for(int j=;;j++)
{
if(j&)
{
if(second[now]==||dis+abs(h[second[now]]-h[now])>x[i])
{
printf("%lld ",suma);
printf("%lld\n",sumb);
break;
}
else
{
suma+=abs(h[second[now]]-h[now]);
dis+=abs(h[second[now]]-h[now]);
now=second[now];
}
}
else
{
if(first[now]==||dis+abs(h[first[now]]-h[now])>x[i])
{
printf("%lld ",suma);
printf("%lld\n",sumb);
break;
}
else
{
sumb+=abs(h[first[now]]-h[now]);
dis+=abs(h[first[now]]-h[now]);
now=first[now];
}
}
}
}
}