【tsinsen A1490】osu!(乔明达) 矩阵+线段树

试题来源

  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;
}

你可能感兴趣的:(【tsinsen A1490】osu!(乔明达) 矩阵+线段树)