【tsinsen A1490】osu!(乔明达) 矩阵+线段树
2016-04-25 20:15
531 查看
试题来源
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; }
相关文章推荐
- 设计模式:观察者模式(Observer)
- 3、JavaScript快速入门(2)
- 图像纹理分析,空洞填补
- 设计模式:观察者模式(Observer)
- 关于libevent的环境搭建和编译使用问题
- 站立会议第七天
- 初学Ajax(二)
- C++ 11(三)
- 跟着编程之美学算法——最长递增子序列
- HDU 1282 回文数猜想(数学题)
- LeetCode之Counting Bits
- 每日Scrum(7)
- java---Serializable(序列化)
- 电动机绕组的接线舞曲
- 机器学习之多变量线性回归(Linear Regression with multiple variables)
- hdu 1076 An Easy Task
- android 语音合成的简单实现
- 如何教女朋友一起编程
- 程序员的自我修养_之三_曾国藩与左宗棠
- 个人工作总结07