D A Y 3 DAY3 DAY3
T 1 T1 T1
学 习 神 技 学习神技 学习神技
王仙女打开了《葵花宝典》,第一页上赫然写道:欲练此功,必先学习上古神技:等比数列求和!
王仙女心想:等比数列是什么**东西?难道我的修仙之路要止步于此了吗?
还好,天无绝人之路,在宝典的第二页上,写着密密麻麻的等比数列的介绍:
等比数列为这样一个数列,它的首项为a_1,第i项为a_1*q^(i-1),其中q为此等比数列的公比。等比数列前n项的求和公式为:
聪慧过人的仙女一下子就学会了等比数列的求和,可是他发现宝典上还有M道测试题,可是这M道的n都非常的大,仙女一时间算不出来,只好打通时空隧道向未来的你求助。有现代化计算机帮助的你一定能很快的求出这M个答案的对吧?
Input
第一行一个正整数M,为询问组数
接下来M行,每行三个正整数a_1,q,n,含义如题。
Output
共M行,为M个询问的答案。答案对〖10〗^9+7取模。
Sample Input
1
2 3 3
Sample Output
26
Data Constraint
对于30%的数据,M≤1000,对于每一个询问,n≤1000
对于100%的数据,
M
≤
[
10
]
5
M ≤ [10]^5
M≤[10]5,
a
1
,
p
≤
1
0
9
a_1,p ≤ 10^9
a1,p≤109,
n
≤
1
0
18
n ≤ 10^{18}
n≤1018
本来考场一旦看到数论题基本就是放弃,顶多打个暴力。可是一看这题非常可做啊,题目非常良心的给出了等比公式的求法,那么帮我这个记不住公式的蒟蒻成功去除了这个唯一的困难。那么看到这个式子先随便举了几个例子,不难发现通过正负性的判定可以将(对于
q
!
=
1
q!=1
q!=1)式子变形为
S
n
=
a
1
∗
(
q
n
−
1
)
/
(
q
−
1
)
S_n = a_1 * (q^n - 1) / (q - 1)
Sn=a1∗(qn−1)/(q−1)。那么这就是一道快速幂,逆元的水题了。有因为模数为
1
e
9
+
7
1e9 + 7
1e9+7,所以逆元可以直接用
b
p
−
2
b^{p - 2}
bp−2来求,对于
n
=
1
n = 1
n=1的情况应该就是入门水平了吧。
AC Code:
#include <cstdio>
#define ll long long
using namespace std;
const int mo = 1e9 + 7;
int m;
ll a,q,n,ans;
ll ksm(ll a,ll b,ll mod)
{
ll res = 1;
a %= mod;
while (b)
{
if (b % 2) res = (res * a) % mod;
b /= 2;
a = (a * a) % mod;
}
return res;
}
int main()
{
scanf("%d",&m);
while (m --)
{
scanf("%lld%lld%lld",&a,&q,&n);
if (q == 1)
{
printf("%lld\n",(a * (n % mo)) % mo);
continue;
}
ans = (a * (ksm(q,n,mo) - 1)) % mo;
ans = (ans * (ksm(q - 1,mo - 2,mo))) % mo;
printf("%lld\n",ans);
}
return 0;
}
T 2 T2 T2
淬 炼 神 体 淬炼神体 淬炼神体
王仙女将你提供的答案填在《葵花宝典》上,突然,宝典发出耀眼的白光,一股强大的吸力瞬间将仙女吸入宝典中。
一阵眩晕过后,仙女发现自己来到了一个浮岛上,四周的半空中也有许多大小不一的浮岛,他抬头一看,空中浮现着这样一句话:欲要成神,必先成就神体。每座浮岛都有一定的淬炼肉体的功效,但你只能选择恰好K座来淬炼你的肉体。最终你的神体有多强,就看你的造化了。
仙女看了看手中的宝典,发现上面浮现着这些浮岛的信息,每座浮岛上有两个数字。仙女研究了一会儿终于明白了,原来,每来到一座浮岛,你就会由这座岛上的神雷淬炼肉体,得到a点淬炼值,但是这种淬炼方法对身体又有一定的伤害,于是你会受到b点损伤值。而最后你的神体强度恰好为你得到的淬炼值之和与你受到的损伤值之和的比值。即神体强度=(∑a)/(∑b)。同时,你不能选择一座浮岛两次。你不必担心仙女如何到达其它的岛,宝典拥有将他传送至任何一座岛的能力。
现在,仙女想知道,他有可能淬炼出的神体的最大强度是多少。他把这个问题交给了未来的你作为刚刚帮助过他的答谢。
Input
第一行包含两个整数N,K,分别表示浮岛的数量(不包括仙女所在的浮岛)和仙女可以选择的浮岛数,浮岛的编号为1~N
第二行包含N个整数,第i个数为编号为i的浮岛的淬炼值a
第二行包含N个整数,第i个数为编号为i的浮岛的损伤值b
Output
一个实数,为仙女有可能淬炼出的神体的最大强度,保留三位小数。
Sample Input
3 2
5 7 1
4 3 2
Sample Output
1.714
Data Constraint
对于30%的数据,2≤K≤N≤10,1≤a,b≤5
对于100%的数据,2≤K≤N≤100000,1≤a,b≤1000
对于我这样一个蒟蒻,感觉考场这题没想到正解真的是非常遗憾啊,我直接打了一个正确性错误的暴力上去,即按照 a i / b i a_i / b_i ai/bi从大到小排序,然后直接取前K大,那么错误的方法,我都做好了暴零的打算,可是竟然侥幸得到了20pts,真的是非常幸运啊。
考后一看题解,这是何等水题。首先二分枚举答案,and then因为最后判定的是 ∑ i = 1 k a i / ∑ i = 1 k b i > = m i d \sum\limits^{k}_{i = 1}a_i / \sum\limits^{k}_{i = 1}b_i >= mid i=1∑kai/i=1∑kbi>=mid,所以可以转换一下就变成了 ∑ i = 1 k a i − ∑ i = 1 k b i ∗ m i d > = 0 \sum\limits^{k}_{i = 1}a_i - \sum\limits^{k}_{i = 1}b_i * mid >= 0 i=1∑kai−i=1∑kbi∗mid>=0那么就直接按照 a i − b i ∗ m i d a_i - b_i * mid ai−bi∗mid 排序,然后取前 k k k大相加,判断是否大于零即可。
AC Code
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n,k;
struct Node{
double a,b,now;
} v[maxn];
double l,r,mid;
bool cmp(Node a,Node b) {return a.now > b.now;}
bool check(double x)
{
double res = 0;
for (int i = 1; i <= n; i ++) v[i].now = v[i].a - x * v[i].b;
sort(v + 1,v + 1 + n,cmp);
for (int i = 1; i <= k ; i ++) res += v[i].now;
if (res >= 0) return 1; else return 0;
}
int main()
{
scanf("%d%d",&n,&k);
for (int i = 1; i <= n; i ++) scanf("%lf",&v[i].a);
for (int i = 1; i <= n; i ++)
{
scanf("%lf",&v[i].b);
if (v[i].a / v[i].b > r) r = v[i].a / v[i].b;
}
l = 0;
while (r - l > 0.0001)
{
double mid = (l + r) / 2;
if (check(mid)) l = mid + 0.0001; else r = mid;
}
printf("%.3lf",l);
return 0;
}
T 3 T3 T3
寻 找 神 格 寻找神格 寻找神格
淬炼完神体,王仙女被传送到了遥远处一座没有神雷的浮岛上,发现浮岛上除了一扇门以外什么都没有。他来到门前,发现上面写着这样一段话:
一个神出了拥有强大的神体外,还需要一枚神格。然而,想要获得神格没那么简单,除了有实力外还需要有运气。曾经有一个人叫金(jin)字(zi)塔(da),他的神体很强,很壮,可是他根本没有运气,所以最后神格拒绝了他。打开这扇门,你将会进入一个神格创造的空间,在那里,神格将会问你一些问题来测试你解决问题的能力,当然,它的问题将会很难,在你答不出来的时候你可以选择随便猜一个答案,以此来展现你的运气。
王仙女二话不说打开了那扇门,一阵眩晕过后,他来到了一个灰蒙蒙的空间。一个苍老的声音在四周响起:小娃娃,我是一枚存在亿万年的神格,我的上一任主人已经死去百万余年了,我也已经在这里等待了百万年了。能否成为我的主人,让我重现百万年前的风采,就看你的能力和运气了。再问问题之前,我要先跟你讲一件事。成为一个神后,最大的责任便是保护神界的人民,他们都出生在神界,但并不都具有神的实力。当然,神界人族的内部也有战争,他们一共分为N个部落,每两个部落之间都有可能发生战争。为了不然神界人族因为战争而损失惨重,神界的诸神将这些部落编号为1~N,当这些部落的人数差距太大时,诸神便会降临,将一些部落的人带走,并放一些在别的部落中。而衡量所有部落人数差距的数值便是方差。接下来,我会告诉你一些部落的人数增加或减少的信息,并会不时的询问你编号为L ~ R的部落的总人数或是他们部落人数的方差。
Input
第一行包含两个正整数N,Q,表示部落数和神格的信息数与询问数总和。
第二行包含N个数,第i个数a_i表示编号为i的部落最初的人数。
接下来Q行,第一个数为t。
当t=0时,这一行还有两个数a,b,表示编号为a的部落增加了b个人(如果b<0则表示减少了|b|个人)。
当t=1时,这一行还有三个数a,b,c,表示编号为a~b的部落增加了c个人(如果c<0则表示减少了|c|个人)。
当t=2时,这一行还有两个数a,b,表示神格询问了编号为a~b的部落现在的总人数。
当t=3时,这一行还有两个数a,b,表示神格询问了编号为a~b的部落人数的方差。
Output
对于每个t=2,输出一行,包含一个整数,表示总人数。
对于每个t=3,输出一行,包含一个实数,表示方差,结果保留三位小数。
Sample Input
5 5
1 2 3 4 5
0 3 3
1 2 3 6
2 3 5
0 1 2
3 1 5
Sample Output
21
10.640
Data Constraint
对于30%的数据,N≤1000,Q≤1000
对于100%的数据,1≤N≤100000,1≤Q≤100000,|a_i |≤1000,数据保证在任何时候|所有部落总人数|≤〖10〗^9
注:由于神界人族的人数统计是用实际人数减去一个标准值,所以人数可能会出现负数
Hint
方差的定义:
求
N
N
N个数的方差,设这
N
N
N个数的平均数为
a
v
e
ave
ave,第i个数为
x
i
x_i
xi
方差
S
=
1
/
n
[
(
x
1
−
a
v
e
)
2
+
(
x
2
−
a
v
e
)
2
+
⋯
+
(
x
n
−
1
−
a
v
e
)
2
+
(
x
n
−
a
v
e
)
2
]
S=1/n \ [(x_1-ave)^2+(x_2-ave)^2+⋯+(x_{n-1}-ave)^2+(x_n-ave)^2]
S=1/n [(x1−ave)2+(x2−ave)2+⋯+(xn−1−ave)2+(xn−ave)2]
考场当我打到这道题是仅剩 30 30 30min,一看,这不是线段树吗,没管三七二十一,啪啪啪就花十五分钟把方差以前的线段树内容全部都敲上了,打到方差的那个瞬间忽然懵比,其实假如我往下推大概也就三分钟就可以推出来了,可是时间不允许我这样做啊,我就用剩下的十分钟干脆打了个暴力交了,考试毕竟拿分重要。最后也拿了20pts。
赛后,自己推了一下,其实很简单。首先是拆开求方差的公式
S
=
1
/
n
[
(
x
1
−
a
v
e
)
2
+
(
x
2
−
a
v
e
)
2
+
⋯
+
(
x
n
−
1
−
a
v
e
)
2
+
(
x
n
−
a
v
e
)
2
]
=
1
/
n
[
x
1
2
+
a
v
e
2
−
2
∗
a
v
e
∗
x
1
+
x
2
2
+
a
v
e
2
−
2
∗
a
v
e
∗
x
2
+
⋯
+
x
n
−
1
2
+
a
v
e
2
−
2
∗
a
v
e
∗
x
n
−
1
+
x
n
2
+
a
v
e
2
−
2
∗
a
v
e
∗
x
n
]
=
1
/
n
[
x
1
2
+
x
2
2
+
⋯
+
x
n
−
1
2
+
x
n
2
+
n
∗
a
v
e
2
−
2
a
v
e
∗
∑
i
=
L
R
x
i
]
S = 1/n \ [(x_1-ave)^2+(x_2-ave)^2+⋯+(x_{n-1}-ave)^2+(x_n-ave)^2] \\\\\\\\\\\\\\= 1 / n \ [x_1^2 + ave^2 - 2*ave*x_1 + x_2^2 + ave^2 - 2*ave*x_2 + ⋯ + x_{n - 1}^2 + ave^2 - 2*ave*x_{n - 1} +x_n^2 + ave^2 - 2*ave*x_n] \\\\\\\\\\\\= 1 / n \ [x_1^2 +x_2^2 + ⋯ +x_{n - 1}^2 + x_n^2 + n * ave^2 - 2ave * \sum\limits^{R}_{i = L} x_i]
S=1/n [(x1−ave)2+(x2−ave)2+⋯+(xn−1−ave)2+(xn−ave)2]=1/n [x12+ave2−2∗ave∗x1+x22+ave2−2∗ave∗x2+⋯+xn−12+ave2−2∗ave∗xn−1+xn2+ave2−2∗ave∗xn]=1/n [x12+x22+⋯+xn−12+xn2+n∗ave2−2ave∗i=L∑Rxi],那么接下来我们在普通线段树的基础上需要做的仅仅是多维护一个平方和,至于平方和其实也并不难,只要我们心平气和地稍作思考。
首先随便举了几个例子,不难发现对于一定的
s
u
m
sum
sum,加上或减去一个数对该平方和的影响是一定的。且很容易推出,当加一个
k
k
k时,对于平方和的影响为
(
x
1
+
k
)
2
+
(
x
2
+
k
)
2
+
⋯
+
(
x
n
−
1
+
k
)
2
+
(
x
n
+
k
)
2
(x_1 + k)^2 + (x_2 + k)^2 + ⋯ +(x_{n - 1} + k)^2 +(x_n + k)^2
(x1+k)2+(x2+k)2+⋯+(xn−1+k)2+(xn+k)2,and then 拆开得
x
1
2
+
k
2
+
2
∗
k
∗
x
1
+
x
2
2
+
k
2
+
2
∗
k
∗
x
2
+
⋯
+
x
n
−
1
2
+
k
2
+
2
∗
k
∗
x
n
−
1
+
x
n
2
+
k
2
+
2
∗
k
∗
k
n
x_1^2 + k^2 + 2 * k * x_1 + x_2^2 + k ^ 2 + 2 * k * x_2 + ⋯ + x_{n - 1} ^ 2 + k ^ 2 + 2 * k * x_{n - 1} + x_n^2 + k^2 + 2 * k *k_n
x12+k2+2∗k∗x1+x22+k2+2∗k∗x2+⋯+xn−12+k2+2∗k∗xn−1+xn2+k2+2∗k∗kn
再合并得
∑
i
=
1
n
x
i
2
+
2
∗
k
∗
∑
i
=
L
R
x
i
+
n
∗
k
2
\sum\limits^{n}_{i = 1} x_i^2 + 2 * k * \sum\limits^{R}_{i = L} x_i + n * k^2
i=1∑nxi2+2∗k∗i=L∑Rxi+n∗k2。终于功德圆满了,上代码。
AC Code
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
int n,q,v[maxn],p,a,b,c;
ll t[maxn * 50],s[maxn * 50],tag[maxn * 50];
double u,ans,ba;
int read()
{
int x = 0,w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
return x * w;
}
void build(int x,int l,int r)
{
if (l == r) {t[x] = 1ll * v[l]; s[x] = 1ll * v[l] * v[l]; return;}
int mid = (l + r) >> 1;
build(x << 1,l,mid);
build(x << 1 | 1,mid + 1,r);
t[x] = t[x << 1] + t[x << 1 | 1];
s[x] = s[x << 1] + s[x << 1 | 1];
}
void down(int x,int l,int r)
{
int mid = (l + r) >> 1;
tag[x << 1] += tag[x];
tag[x << 1 | 1] += tag[x];
s[x << 1] += 2 * tag[x] * t[x << 1] + 1ll * (mid - l + 1) * tag[x] * tag[x];
s[x << 1 | 1] += 2 * tag[x] * t[x << 1 | 1] + 1ll * (r - mid) * tag[x] * tag[x];
t[x << 1] += tag[x] * 1ll * (mid - l + 1);
t[x << 1 | 1] += tag[x] * 1ll * (r - mid);
tag[x] = 0;
}
void update(int x,int l,int r,int L,int R,int k)
{
if (L <= l && R >= r)
{
tag[x] += 1ll * k;
s[x] += 1ll * 2 * k * t[x] + 1ll * (r - l + 1) * k * k;
t[x] += 1ll * k * (r - l + 1);
return;
}
if (tag[x]) down(x,l,r);
int mid = (l + r) >> 1;
if (L <= mid) update(x << 1,l,mid,L,R,k);
if (R > mid) update(x << 1 | 1,mid + 1,r,L,R,k);
t[x] = t[x << 1] + t[x << 1 | 1];
s[x] = s[x << 1] + s[x << 1 | 1];
}
ll query(int x,int l,int r,int L,int R)
{
if (L <= l && R >= r) return t[x];
ll res = 0;
int mid = (l + r) >> 1;
if (tag[x]) down(x,l,r);
if (L <= mid) res += query(x << 1,l,mid,L,R);
if (R > mid) res += query(x << 1 | 1,mid + 1,r,L,R);
return res;
}
ll querys(int x,int l,int r,int L,int R)
{
if (L <= l && R >= r) return s[x];
ll res = 0;
int mid = (l + r) >> 1;
if (tag[x]) down(x,l,r);
if (L <= mid) res += querys(x << 1,l,mid,L,R);
if (R > mid) res += querys(x << 1 | 1,mid + 1,r,L,R);
return res;
}
int main()
{
n = read(),q = read();
for (int i = 1; i <= n; i ++) v[i] = read();
build(1,1,n);
while (q --)
{
p = read();
if (p == 0)
{
a = read(),b = read();
update(1,1,n,a,a,b);
}
if (p == 1)
{
a = read(),b = read(),c = read();
update(1,1,n,a,b,c);
}
if (p == 2)
{
a = read(),b = read();
printf("%lld\n",query(1,1,n,a,b));
}
if (p == 3)
{
a = read(),b = read();
ans = 0;
u = 1.000 * query(1,1,n,a,b);;
ba = u / (b - a + 1);
ans = querys(1,1,n,a,b) + (b - a + 1) * ba * ba - 2 * ba * u;
ans /= (b - a + 1);
printf("%.10lf\n",ans);
}
}
return 0;
}
此处还有一个非常神奇的坑点,便是精度问题,最后要取十位小数。
最终得分
100
+
20
+
20
100 + 20 + 20
100+20+20,排名也还算客观。
Ps.经过几篇博客的磨练,感觉格式好看多了,继续加油。