【BZOJ5020】[THUWC2017] 在美妙的数学王国中畅游

这篇博客介绍了如何解决一个数学问题,涉及到一个森林中的函数值求和,其中函数值在[0,1]范围内。通过拉格朗日中值定理和泰勒展开式,利用线性链表(LCT)来维护路径上的函数值之和,处理非多项式函数。博主分享了解决方案,并提到了在无法求导的情况下如何应对。" 112949781,10293389,Python爬虫:解析与获取网页表格数据,"['Python爬虫', '数据分析', '网页抓取']
摘要由CSDN通过智能技术生成

题目链接

题意

一个森林,支持加边删边
每一个点上有一个关于x的函数,函数值在[0,1]内
每次给出一个x并询问一条路径上所有点的函数值的和,x也是[0,1]

Sol

所有人智商为1时,直接算函数值并用LCT维护路径即可
如果只有一次函数就维护 a ∑ a b ∑ b

然后就是怎么搞这两个不是多项式的函数了

为什么不好处理,因为我们不能通过x的次数相同的把和x有关的东西提取出来预先算好

于是恶补一发数学

可以把一个非多项式函数在一个x=x0的地方把它展开成多项式函数

若函数 f(x) f ( x ) n n 阶导数在[a,b]区间内连续,则对 f(x) f ( x ) x=x0(x0[a,b]) x = x 0 ( x 0 ∈ [ a , b ] ) 处使用 n n 次拉格朗日中值定理可以得到带拉格朗日余项的泰勒展开式

泰勒展开式:

f(x)=f(x0)+f(x0) (xx0)1!+f(x0) (xx0)22!+f(n)(x0)(xx0)nn!

既然x的范围是 [a,b] [ a , b ] ,且三个函数都可导,那么显然取 x0=0 x 0 = 0

这样我们就可以愉快地合并每一个函数相同次数的a和b了,用LCT维护和即可
最后统计答案的时候每一个项乘上x的对应次数再除以阶乘就可以了,求导的次数大概14左右就行了

我们是不是还漏了点东西

不会求导怎么办?GG

要用到的基本的求导公式:

(ax)=axln(a)(ex)=ex(ax+b)=asin(x)=cos(x)cos(x)=sin(x)(sin(x))=cos(x)(cos(x))=sin(x)f(g(x))=g(x)f(g(x)) () { ( a x ) ′ = a x l n ( a ) ( e x ) ′ = e x ( a x + b ) ′ = a s i n ( x ) ′ = c o s ( x ) c o s ( x ) ′ = − s i n ( x ) ( − s i n ( x ) ) ′ = − c o s ( x ) ( − c o s ( x ) ) ′ = s i n ( x ) f ( g ( x ) ) ′ = g ( x ) ′ f ′ ( g ( x ) )   ( 复 合 函 数 求 导 )

于是题目中的三个函数就是两个复合函数,一个不需要求导就能做的一次函数

泰勒展开式+LCT乱搞就行了(OWO)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
int n,m;
char tp[20];
namespace Link_Cut_Tree{
#define ls son[0]
#define rs son[1]
#define __ NULL
#define get_son(a) (a->fa->rs==a)
#define get_sum(i,a) (a==__? 0:a->sum[i])
    typedef double db;
    const int D=16;
    db fac[20];//预处理阶乘
    const int N=1e5+10;
    struct node{
        db sum[D];node* son[2];node* fa;bool is_root;bool rev;db a,b;
        int f;
        void clear(){is_root=1;fa=__;ls=rs=__;rev=0;a=b=0;f=0;Set(sum,0);}
    }pool[N<<1];int cnt=0;
    node* rt[N];
    inline void calc(node* p){
        register db a=p->a,b=p->b;
        register db c=1.00000;
        if(p->f==1){
            register db si=sin(b),cs=cos(b);
            for(register int i=0;i<D;++i){
                if(i%4==0) p->sum[i]+=c*si;
                else if(i%4==1) p->sum[i]+=c*cs;
                else if(i%4==2) p->sum[i]+=-c*si;
                else p->sum[i]+=-c*cs;
                c*=a;
            }
        }
        else if(p->f==2){
            register db ex=exp(b);
            for(register int i=0;i<D;++i){
                p->sum[i]+=ex*c;
                c*=a;
            }
        }
        else p->sum[0]+=b,p->sum[1]+=a;
        return ;
    }
    inline void init(){
        register int tp;register db a,b;
        for(register int i=1;i<=n;++i){rt[i]=&pool[++cnt];rt[i]->clear();}
        for(register int i=1;i<=n;++i){
            scanf("%d %lf %lf",&tp,&a,&b);
            rt[i]->f=tp;rt[i]->a=a;rt[i]->b=b;calc(rt[i]);
        }
        return;
    }
    node* st[N];int top=0;
    inline void down(node* p){
        if(p==__) return;if(!p->rev) return;p->rev=0;
        if(p->ls!=__) p->ls->rev^=1;
        if(p->rs!=__) p->rs->rev^=1;
        swap(p->ls,p->rs);
        return;
    }
    inline void push_down(node* p){
        top=0;while(!p->is_root) st[++top]=p,p=p->fa;down(p);
        while(top) down(st[top--]);
        return;
    }
    inline void update(node* p){//更新
        if(p==__) return;
        for(register int i=0;i<D;++i) p->sum[i]=get_sum(i,p->ls)+get_sum(i,p->rs);
        calc(p);
        return;
    }
    inline void rotate(node* p)
    {
        if(p->is_root) return;
        register node* q=p->fa;register node* gp=q->fa;
        register int k=get_son(p);
        q->son[k]=p->son[k^1];
        if(p->son[k^1]!=__) p->son[k^1]->fa=q;
        p->fa=gp;
        if(q->is_root) q->is_root=0,p->is_root=1;
        else if(gp!=__) gp->son[get_son(q)]=p;
        q->fa=p;p->son[k^1]=q;
        update(q);
        return;
    }
    inline void Splay(node* p){
        if(p==__) return;push_down(p);
        for(;!p->is_root;rotate(p)){
            if(p->fa->is_root) continue;
            if(get_son(p)==get_son(p->fa)) rotate(p->fa);
            else rotate(p);
        }
        update(p);
        return ;
    }
    inline void access(node* p){
        register node* pre=__;
        for(;p!=__;pre=p,p=p->fa){
            Splay(p);
            if(p->rs!=__) p->rs->is_root=1;p->rs=pre;
            if(pre!=__) pre->is_root=0;
            update(p);
        }
        return ;
    }
    inline void make_root(node* p){access(p);Splay(p);p->rev^=1;return;}
    inline void Link(node* p,node* q){make_root(p);p->fa=q;return;}
    inline void split(node* p,node* q){make_root(p);access(q);Splay(q);return;}
    inline void Cut(node* p,node* q){split(p,q);if(q->ls==p) q->ls=__,p->fa=__,p->is_root=1;return;}
    inline node* find(node* p){for(access(p),Splay(p);p->ls!=__;p=p->ls);return p;}
    void work(){
        init();fac[0]=1;
        for(register int i=1;i<D;++i) fac[i]=fac[i-1]*(db)(i);
        for(register int i=1;i<=m;++i){
            scanf("%s",tp+1);register int u,v;register db a,b;
            if(tp[1]=='a') {
                scanf("%d %d",&u,&v);
                Link(rt[++u],rt[++v]);
            }
            else if(tp[1]=='d') {
                scanf("%d %d",&u,&v);
                Cut(rt[++u],rt[++v]);
            }
            else if(tp[1]=='m') {
                scanf("%d %d %lf %lf",&u,&v,&a,&b);++u;Splay(rt[u]);
                rt[u]->f=v;rt[u]->a=a;rt[u]->b=b;update(rt[u]);
            }
            else {
                scanf("%d %d %lf",&u,&v,&a);++u,++v;
                if(find(rt[v])!=find(rt[u])) puts("unreachable");
                else {
                    split(rt[u],rt[v]);
                    register db ans=rt[v]->sum[0];register db x=a;
                    for(register int i=1;i<D;++i) ans+=x*rt[v]->sum[i]/fac[i],x*=a;//同次数函数组合求和即可
                    printf("%.9e\n",ans);
                }
            }
        }
    }
}
int main()
{
    scanf("%d %d %s",&n,&m,tp+1);
    return Link_Cut_Tree::work(),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值