清华冬令营的某题

17 篇文章 0 订阅
5 篇文章 0 订阅

问题背景

数字和数学规律主宰着这个世界。
机器的运转,
生命的消长,
宇宙的进程,
这些神秘而又美妙的过程无不可以用数学的语言展现出来。
这印证了一句古老的名言:
“学好数理化,走遍天下都不怕。”

问题描述

学渣小R被大学的数学课程虐得生活不能自理,微积分的成绩曾是他在教室里上的课的最低分。然而他的某位陈姓室友却能轻松地在数学考试中得到满分。为了提升自己的数学课成绩,有一天晚上(在他睡觉的时候),他来到了数学王国。

数学王国中,每个人的智商可以用一个属于 [0,1] 的实数表示。数学王国中有 n 个城市,编号从0 n1 ,这些城市由若干座魔法桥连接。每个城市的中心都有一个魔法球,每个魔法球中藏有一道数学题。每个人在做完这道数学题之后都会得到一个在 [0,1] 区间内的分数。一道题可以用一个从 [0,1] 映射到 [0,1] 的函数 f(x) 表示。若一个人的智商为 x ,则他做完这道数学题之后会得到f(x)分。函数 f 有三种形式:

  1. 正弦函数sin(ax+b) (a[0,1],b[0,π],a+b[0,π])

    • 指数函数 eax+b (a[1,1],b[2,0],a+b[2,0])
    • 一次函数 ax+b (a[1,1],b[0,1],a+b[0,1])
    • 数学王国中的魔法桥会发生变化,有时会有一座魔法桥消失,有时会有一座魔法桥出现。但在任意时刻,只存在至多一条连接任意两个城市的简单路径(即所有城市形成一个森林)。在初始情况下,数学王国中不存在任何的魔法桥。

      数学王国的国王拉格朗日很乐意传授小R数学知识,但前提是小R要先回答国王的问题。这些问题具有相同的形式,即一个智商为 x 的人从城市u旅行到城市 v (即经过u v 这条路径上的所有城市,包括u v )且做了所有城市内的数学题后,他所有得分的总和是多少。

      输入格式

      第一行两个正整数 n,m和一个字符串 type 。表示数学王国中共有 n 座城市,发生了m个事件,该数据的类型为 type type 字符串是为了能让大家更方便地获得部分分,你可能不需要用到这个输入。其具体含义在限制与约定中有解释。

      接下来 n 行,第i行表示初始情况下编号为 i 的城市的魔法球中的函数。一个魔法用一个整数 f 表示函数的类型,两个实数 a,b 表示函数的参数,若

      1. f=1 ,则函数为 f(x)=sin(ax+b)(a[0,1],b[0,π],a+b[0,π])
      2. f=2 ,则函数为 f(x)=eax+b(a[1,1],b[2,0],a+b[2,0])
      3. f=3 ,则函数为 f(x)=ax+b(a[1,1],b[0,1],a+b[0,1])

      接下来 m 行,每行描述一个事件,事件分为四类。

      1. appear u v 表示数学王国中出现了一条连接u v 这两座城市的魔法桥(0u,v<n,uv) ,保证连接前 u v这两座城市不能互相到达。

      2. disappear u v 表示数学王国中连接 u v这两座城市的魔法桥消失了,保证这座魔法桥是存在的。
      3. magic c f a b 表示城市 c 的魔法球中的魔法变成了类型为f,参数为 a,b 的函数
      4. travel u v x 表示询问一个智商为 x 的人从城市u旅行到城市 v (即经过u v 这条路径上的所有城市,包括u v )后,他得分的总和是多少。若无法从u到达 v ,则输出一行一个字符串 unreachable

      输出格式

      对于每个询问,输出一行实数,表示得分的总和。

      限制与约定

      对于100%的数据,1n100000,1m200000
      本题共有20个数据点,每个数据点5分。
      时限:5s

      数据类型的含义:
      A:不存在 disappear 事件,且所有appear事件中的 u=v1
      B:不存在 disappear 事件
      C:所有的 travel 事件经过的城市总数 5000000 (不可到达的城市对不计入在内)
      D:无限制
      0:所有 travel 事件中, x=1 (即所有人的智商均为 1
      1:无限制

      评分标准

      如果你的答案与标准答案的相对误差在107以内或绝对误差在 107 以内,则被判定为正确。

      如果你的所有答案均为正确,则得满分,否则得0分。

      请注意输出格式:每行输出一个答案,答案只能为 unreachable 或者一个实数(建议使用科学计数法表示)。每行的长度不得超过50。错误输出格式会被判定为0分。

      小R教你学数学

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

      f(x)=f(x0)+f(x0)(xx0)1!+f(x0)(xx0)22!++f(n1)(x0)(xx0)n1(n1)!+f(n)(ξ)(xx0)nn!,x[a,b]

      其中,当 x>x0 时, ξ[x0,x] 。当 x<x0 时, ξ[x,x0]

      f(n) 表示函数 f n阶导数

      Solution

      LCT的题目,比较裸。关键是怎么维护函数的性质。
      考虑题目后面告诉我们的数学知识,我们可以把函数转换成k次多项式系数表示的方式,这三个函数的k阶导数都比较好求,为了好算我们取公式中的 x0 为0,然后只需要保存下来每个节点函数的k次项系数。本人亲测k要取10以上才能保证精度。这样的话维护系数很方便。为了提高精度我们还可以把式子中的阶乘项在统计答案的时候再除(不过好像没必要)。
      然后就是普通的LCT操作了……

      代码:

      #include<cstdio>
      #include<cmath>
      #include<cstring>
      #include<algorithm>
      using namespace std;
      
      inline int read(){
          int xx=0,f=1;char ch=getchar();
          for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
          for(;ch>='0'&&ch<='9';ch=getchar())xx=xx*10+ch-'0';
          return xx*f;
      }
      
      const int maxn=100010,max0=10;
      struct node{
          int fa,ch[2];
          double sum[max0+1],data[max0+1];
          bool is_root,reverse;
      }T[maxn];
      int n,m,num;
      char type[maxn];
      
      int getson(int x){
          return (x==T[T[x].fa].ch[1]);
      }
      void pushreverse(int x){
          if(!x)return;
          swap(T[x].ch[0],T[x].ch[1]);
          T[x].reverse^=1;
      }
      void pushdown(int x){
          if(T[x].reverse){
              pushreverse(T[x].ch[0]);
              pushreverse(T[x].ch[1]);
              T[x].reverse=false;
          }
      }
      void update(int x){
          memset(T[x].sum,0,sizeof T[x].sum);
          for(int i=0;i<=max0;i++)T[x].sum[i]+=T[x].data[i];
          if(T[x].ch[0]){
              for(int i=0;i<=max0;i++)T[x].sum[i]+=T[T[x].ch[0]].sum[i];
          }
          if(T[x].ch[1]){
              for(int i=0;i<=max0;i++)T[x].sum[i]+=T[T[x].ch[1]].sum[i];
          }
      }
      void rotate(int x){
          if(T[x].is_root)return;
          int k=getson(x),fa=T[x].fa,fafa=T[fa].fa;
          pushdown(fa);pushdown(x);
          T[fa].ch[k]=T[x].ch[k^1];
          if(T[x].ch[k^1])T[T[x].ch[k^1]].fa=fa;
          T[x].ch[k^1]=fa;T[fa].fa=x;
          T[x].fa=fafa;
          if(!T[fa].is_root)T[fafa].ch[fa==T[fafa].ch[1]]=x;
          else T[x].is_root=true,T[fa].is_root=false;
          update(fa);update(x);
      }
      void push(int x){
          if(!T[x].is_root)push(T[x].fa);
          pushdown(x);
      }
      void Splay(int x){
          push(x);
          for(int fa;!T[x].is_root;rotate(x)){
              if(!T[fa=T[x].fa].is_root){
                  rotate((getson(x)==getson(fa))?fa:x);
              }
          }
      }
      void access(int x){
          int y=0;
          do{
              Splay(x);pushdown(x);
              T[T[x].ch[1]].is_root=true;
              T[T[x].ch[1]=y].is_root=false;
              update(x);x=T[y=x].fa;
          }while(x);
      }
      void mroot(int x){
          access(x);Splay(x);pushreverse(x);
      }
      void link(int u,int v){
          mroot(u);T[u].fa=v;
      }
      void cut(int u,int v){
          mroot(u);
          Splay(v);
          T[T[v].ch[0]].fa=T[v].fa;
          T[T[v].ch[0]].is_root=true;
          update(T[v].ch[0]);
          T[v].fa=0;T[v].ch[0]=0;
      }
      bool judge(int u,int v){
          while(T[u].fa)u=T[u].fa;
          while(T[v].fa)v=T[v].fa;
          return u==v;
      }
      void get_sin(int x,double a,double b){
          T[x].data[0]=sin(b);
          double f=1,temp=a;
          for(int i=1;i<=max0;i++,temp*=a){
              if(!(i&1)){
                  f*=-1;
                  T[x].data[i]=f*temp*sin(b);
              }
              else T[x].data[i]=f*temp*cos(b);
          }
      }
      void get_pow(int x,double a,double b){
          double temp=1;
          for(int i=0;i<=max0;i++,temp*=a){
              T[x].data[i]=temp*exp(b);
          }
      }
      void get_funtion(int x,double a,double b){
          T[x].data[0]=b;T[x].data[1]=a;
          for(int i=2;i<=max0;i++)T[x].data[i]=0;
      }
      
      int main(){
          freopen("math.in","r",stdin);
          freopen("math.out","w",stdout);
          n=read();m=read();scanf("%*s");
          for(int i=1;i<=n;i++)T[i].is_root=true;
          for(int i=1,opt;i<=n;i++){
              double a,b;
              opt=read();scanf("%lf%lf",&a,&b);
              if(opt==1)get_sin(i,a,b);
              else if(opt==2)get_pow(i,a,b);
              else get_funtion(i,a,b);
              update(i);
          }
          while(m--){
              int u,v,f;
              double a,b,x;
              scanf("%s",type);
              if(type[0]=='a'){
                  u=read();v=read();
                  link(++u,++v);
              }
              else if(type[0]=='d'){
                  u=read();v=read();
                  cut(++u,++v);
              }
              else if(type[0]=='m'){
                  u=read();f=read();scanf("%lf%lf",&a,&b);
                  Splay(++u);
                  if(f==1)get_sin(u,a,b);
                  else if(f==2)get_pow(u,a,b);
                  else get_funtion(u,a,b);
                  update(u);
              }
              else{
                  u=read();v=read();scanf("%lf",&x);
                  if(judge(++u,++v)){
                      mroot(u);access(v);Splay(u);
                      double ans=0,temp=1,jie=1;
                      for(int i=0;i<=max0;jie*=(++i),temp*=x){
                          ans+=T[u].sum[i]*temp/jie;
                      }
                      printf("%.10lf\n",ans);
                  }
                  else puts("unreachable");
              }
          }
          return 0;
      }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值