问题背景
数字和数学规律主宰着这个世界。
机器的运转,
生命的消长,
宇宙的进程,
这些神秘而又美妙的过程无不可以用数学的语言展现出来。
这印证了一句古老的名言:
“学好数理化,走遍天下都不怕。”
问题描述
学渣小R被大学的数学课程虐得生活不能自理,微积分的成绩曾是他在教室里上的课的最低分。然而他的某位陈姓室友却能轻松地在数学考试中得到满分。为了提升自己的数学课成绩,有一天晚上(在他睡觉的时候),他来到了数学王国。
数学王国中,每个人的智商可以用一个属于
[0,1]
的实数表示。数学王国中有
n
个城市,编号从
- 正弦函数
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 表示函数的参数,若- f=1 ,则函数为 f(x)=sin(ax+b)(a∈[0,1],b∈[0,π],a+b∈[0,π])
- f=2 ,则函数为 f(x)=eax+b(a∈[−1,1],b∈[−2,0],a+b∈[−2,0])
- f=3 ,则函数为 f(x)=ax+b(a∈[−1,1],b∈[0,1],a+b∈[0,1])
接下来 m 行,每行描述一个事件,事件分为四类。
appear u v
表示数学王国中出现了一条连接u 和 v 这两座城市的魔法桥(0≤u,v<n,u≠v) ,保证连接前 u 和v 这两座城市不能互相到达。disappear u v
表示数学王国中连接 u 和v 这两座城市的魔法桥消失了,保证这座魔法桥是存在的。magic c f a b
表示城市 c 的魔法球中的魔法变成了类型为f ,参数为 a,b 的函数travel u v x
表示询问一个智商为 x 的人从城市u 旅行到城市 v (即经过u 到 v 这条路径上的所有城市,包括u 和 v )后,他得分的总和是多少。若无法从u 到达 v ,则输出一行一个字符串unreachable
。
输出格式
对于每个询问,输出一行实数,表示得分的总和。
限制与约定
对于100%的数据,
1≤n≤100000,1≤m≤200000 。
本题共有20个数据点,每个数据点5分。
时限:5s数据类型的含义:
A:不存在disappear
事件,且所有appear
事件中的 u=v−1
B:不存在disappear
事件
C:所有的travel
事件经过的城市总数 ≤5000000 (不可到达的城市对不计入在内)
D:无限制
0:所有travel
事件中, x=1 (即所有人的智商均为 1 )
1:无限制评分标准
如果你的答案与标准答案的相对误差在
10−7 以内或绝对误差在 10−7 以内,则被判定为正确。如果你的所有答案均为正确,则得满分,否则得0分。
请注意输出格式:每行输出一个答案,答案只能为
unreachable
或者一个实数(建议使用科学计数法表示)。每行的长度不得超过50。错误输出格式会被判定为0分。小R教你学数学
若函数 f(x) 的 n 阶导数在
[a,b] 区间内连续,则对 f(x) 在 x0(x0∈[a,b]) 处使用 n 次拉格朗日中值定理可以得到带拉格朗日余项的泰勒展开式f(x)=f(x0)+f′(x0)(x−x0)1!+f″(x0)(x−x0)22!+⋯+f(n−1)(x0)(x−x0)n−1(n−1)!+f(n)(ξ)(x−x0)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; }