2013中国国家集训队第二次作业
osu!是一个基于《押忍!战斗!应援团》《精英节拍特工》《太鼓达人》等各种音乐游戏做成的一款独特的PC版音乐游戏。游戏中,玩家需要根据音乐的节奏,通过鼠标点击或敲击按键合成一首歌曲。
一张osu!的地图是由若干个“音”排列而成的。在本题中,对于每个音我们只需要考虑成功点击和错过(miss)这两种情况。对于一张osu!地图,玩家的完成情况可以用一个01串表示(0代表miss,1代表成功)。在本题中,使用如下计分规则:将玩家完成一张地图的01串中所有的0删去,则这个串可能会断裂成若干段连续的1。对于一段长度为L的1(L≥1),你的总分会增加L^2+L+1。例如:一张地图有10个音,某玩家完成情况为1011101110,则删除所有0后得到的是“1”“111”和“111”。因此这个玩家的得分为(1+1+1)+(9+3+1)+(9+3+1)=29。
ACMonster要给Sandytea出一张osu!的地图。在一张图中,不同音对于Sandytea而言难度可能是不同的。我们定义一个音的难度系数为Sandytea成功完成这个音的概率,因此这个难度系数是介于0和1之间的。
现在ACMonster写下了一个包含N个音的序列,但是他不想直接把这个序列做成地图,而是选择其中的某个片段。设S(X,Y)代表序列上第X个音到第Y个音构成的序列,ACMonster想知道如果把S(X,Y)对应的序列做成地图,Sandytea的期望得分是多少。有时ACMonster会觉得某个音的难度系数不太合理,因此要进行修改。请你想办法处理ACMonster的修改,并回答他提出的问题。
第一行包含两个数N和M,N代表序列中音的个数,M代表询问及修改的总次数。
第二行至第(N+1)行每行包含一个实数,第i行的实数表示第(i-1)个音的难度系数。
第(N+2)行至第(N+M+1)行每行包含三个数Type,X和Y。
如果Type=0,代表ACMonster询问S(X,Y)的期望得分,保证X和Y为整数,1≤X≤Y≤N。
如果Type=1,代表ACMonster要把第X个音的难度系数修改为Y,保证X为整数,1≤X≤N,0≤Y≤1。
对于输入中所有询问,按照出现的顺序输出相应的答案,四舍五入保留两位小数。
2 3
0.5
0.5
0 1 2
1 1 0
0 1 2
3.25
1.50
对于第一次询问,00,01,10,11这四种情况出现的概率均为1/4,得分分别为0,3,3,7。因此期望得分为(0+3+3+7)/4=3.25。
对于第二次询问,00,01这两种情况出现的概率均为1/2,得分分别为0,3。因此期望得分为(0+3)/2=1.50。
20%的数据满足N,M≤5000;
60%的数据满足N,M≤50000;
100%的数据满足N,M≤500000。
小规模数据可以套bzoj4318
满分算法用线段树,分别维护平方、一次方、1,然后1和一次方可以直接合并,平方那个推一下发现是一个线性递推式,可以构造3*3的矩阵。
因为这个题及其卡常,我们发现矩乘的时候真正有用的变量只有四个,所以可以分别维护,减少常数
然而并没有什么卵用,我的代码是75分,被卡常了,求老司机带我卡常
我发现我风格分好高
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
const int SZ = 500010;
const int INF = 1000000010;
struct segment{
int l,r;
double s0,s1,a11,a12,a31,a32,lx,rx;
}tree[SZ << 2];
double a[SZ];
void update(segment &p,const segment &a,const segment &b)
{
p.s0 = a.s0 + b.s0 - a.rx * b.lx;
p.s1 = a.s1 + b.s1;
p.a11 = a.a11 * b.a11;
p.a12 = a.a11 * b.a12 + a.a12;
p.a31 = a.a31 * b.a11 + b.a31;
p.a32 = a.a31 * b.a12 + a.a32 + b.a32;
p.lx = a.lx; p.rx = b.rx;
}
void build(int p,int l,int r)
{
tree[p].l = l,tree[p].r = r;
if(l == r)
{
tree[p].lx = tree[p].rx = tree[p].s1 = tree[p].s0 = a[l];
tree[p].a11 = tree[p].a31 = tree[p].a32 = a[l];
tree[p].a12 = a[l] * 2;
return ;
}
int mid = (l + r) >> 1;
build(p << 1,l,mid); build(p << 1 | 1,mid + 1,r);
update(tree[p],tree[p << 1],tree[p << 1 | 1]);
}
segment ask(int p,int l,int r)
{
if(l == tree[p].l && tree[p].r == r)
return tree[p];
int mid = (tree[p].l + tree[p].r) >> 1;
if(l > mid) return ask(p << 1 | 1,l,r);
else if(r <= mid) return ask(p << 1,l,r);
else
{
segment a = ask(p << 1,l,mid),b = ask(p << 1 | 1,mid + 1,r);
segment ans;
update(ans,a,b);
return ans;
}
}
void change(int p,int pos,double d)
{
if(tree[p].l == tree[p].r)
{
tree[p].lx = tree[p].rx = tree[p].s1 = tree[p].s0 = d;
tree[p].a11 = tree[p].a31 = tree[p].a32 = d;
tree[p].a12 = d * 2;
return ;
}
int mid = (tree[p].l + tree[p].r) >> 1;
if(pos <= mid) change(p << 1,pos,d);
else change(p << 1 | 1,pos,d);
update(tree[p],tree[p << 1],tree[p << 1 | 1]);
}
void scan(int &n)
{
n = 0;
char a = getchar();
while(a < '0' || a > '9') a = getchar();
while(a >= '0' && a <= '9') n = n * 10 + a - '0',a = getchar();
}
int main()
{
int n,m;
scan(n); scan(m);
for(int i = 1;i <= n;i ++)
scanf("%lf",&a[i]);
build(1,1,n);
while(m --)
{
int opt;
scan(opt);
if(opt == 0)
{
int x,y;
scan(x); scan(y);
segment ans = ask(1,x,y);
printf("%.2lf\n",ans.s0 + ans.s1 + ans.a32);
}
else
{
int x; double y;
scan(x);
scanf("%lf",&y);
change(1,x,y);
}
}
return 0;
}