题目描述
输入
输出
样例输入
3 7 C1
1 1 0
3 0.5 0.5
3 -0.5 0.7
appear 0 1
travel 0 1 0.3
appear 0 2
travel 1 2 0.5
disappear 0 1
appear 1 2
travel 1 2 0.5
样例输出
9.45520207e-001
1.67942554e+000
1.20000000e+000
题解
泰勒展开+LCT
如果对于每个子树直接维护函数的话很难维护。
注意到题目给出的函数都是收敛的,因此可以使用泰勒展开公式维护多项式。
本题用到的公式:
$e^x=\sum\limits_{i=0}^\infty \frac{x^i}{i!}\\ \sin x=\sum\limits_{i=0}^\infty (-1)^i\frac{x^{2i+1}}{(2i+1)!}$
要求$ax+b$对应的函数值直接把$ax+b$代入,用二项式定理展开即可。
注意到分母都是阶乘级别的,而$x$在$[0,1]$范围内,因此当$i$大到一定程度时可以忽略不计。因此可以只保留多项式的前几项,本题中保留16项即可保证精度。
剩下的就好办了,直接对LCT中Splay Tree的每个节点维护它的生成多项式和它子树生成多项式的和。查询时直接取出路径对应的生成多项式,把x代入即可出解。
时间复杂度$O(16n\log n+256n)=O(勉强能过)$,稍微有点卡常。。(不过像我这样自带超大常数的,跑了39s卡过了,应该也没什么特别卡的)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define K 16
using namespace std;
struct data
{
double w[K] , sum[K];
int fa , c[2] , rev;
}a[N];
double p1[K] , p2[K] , fac[K] , choose[K][K];
char str[15];
inline void pushup(int x)
{
int i;
for(i = 0 ; i < K ; i ++ ) a[x].sum[i] = a[a[x].c[0]].sum[i] + a[a[x].c[1]].sum[i] + a[x].w[i];
}
inline void pushdown(int x)
{
if(a[x].rev)
{
int l = a[x].c[0] , r = a[x].c[1];
swap(a[l].c[0] , a[l].c[1]) , swap(a[r].c[0] , a[r].c[1]);
a[l].rev ^= 1 , a[r].rev ^= 1 , a[x].rev = 0;
}
}
inline bool isroot(int x)
{
return x != a[a[x].fa].c[0] && x != a[a[x].fa].c[1];
}
void update(int x)
{
if(!isroot(x)) update(a[x].fa);
pushdown(x);
}
inline void rotate(int x)
{
int y = a[x].fa , z = a[y].fa , l = (a[y].c[1] == x) , r = l ^ 1;
if(!isroot(y)) a[z].c[a[z].c[1] == y] = x;
a[x].fa = z , a[y].fa = x , a[a[x].c[r]].fa = y , a[y].c[l] = a[x].c[r] , a[x].c[r] = y;
pushup(y) , pushup(x);
}
inline void splay(int x)
{
update(x);
int y , z;
while(!isroot(x))
{
y = a[x].fa , z = a[y].fa;
if(!isroot(y))
{
if((a[y].c[0] == x) ^ (a[z].c[0] == y)) rotate(x);
else rotate(y);
}
rotate(x);
}
}
inline void access(int x)
{
int t = 0;
while(x) splay(x) , a[x].c[1] = t , pushup(x) , t = x , x = a[x].fa;
}
inline int findroot(int x)
{
while(a[x].fa) x = a[x].fa;
return x;
}
inline void makeroot(int x)
{
access(x) , splay(x) , swap(a[x].c[0] , a[x].c[1]) , a[x].rev ^= 1;
}
inline void link(int x , int y)
{
makeroot(x) , a[x].fa = y;
}
inline void split(int x , int y)
{
makeroot(x) , access(y) , splay(y);
}
inline void cut(int x , int y)
{
split(x , y) , a[x].fa = a[y].c[0] = 0 , pushup(y);
}
void work(int x , int f , double a1 , double a2)
{
memset(a[x].w , 0 , sizeof(a[x].w));
int i , j;
for(i = 1 ; i < K ; i ++ ) p1[i] = p1[i - 1] * a1 , p2[i] = p2[i - 1] * a2;
if(f == 1)
{
for(i = 1 ; i < K ; i += 4)
for(j = 0 ; j <= i ; j ++ )
a[x].w[j] += p1[j] * p2[i - j] * choose[i][j] / fac[i];
for(i = 3 ; i < K ; i += 4)
for(j = 0 ; j <= i ; j ++ )
a[x].w[j] -= p1[j] * p2[i - j] * choose[i][j] / fac[i];
}
else if(f == 2)
{
for(i = 0 ; i < K ; i ++ )
for(j = 0 ; j <= i ; j ++ )
a[x].w[j] += p1[j] * p2[i - j] * choose[i][j] / fac[i];
}
else a[x].w[1] = a1 , a[x].w[0] = a2;
}
inline void init()
{
int i , j;
p1[0] = p2[0] = fac[0] = 1;
for(i = 1 ; i < K ; i ++ ) fac[i] = fac[i - 1] * i;
for(i = 0 ; i < K ; i ++ )
{
choose[i][0] = 1;
for(j = 1 ; j <= i ; j ++ ) choose[i][j] = choose[i - 1][j] + choose[i - 1][j - 1];
}
}
int main()
{
init();
int n , m , i , f , x , y;
double a1 , a2 , ans;
scanf("%d%d%*s" , &n , &m);
for(i = 1 ; i <= n ; i ++ ) scanf("%d%lf%lf" , &f , &a1 , &a2) , work(i , f , a1 , a2) , pushup(i);
while(m -- )
{
scanf("%s" , str);
switch(str[0])
{
case 'a': scanf("%d%d" , &x , &y) , x ++ , y ++ , link(x , y); break;
case 'd': scanf("%d%d" , &x , &y) , x ++ , y ++ , cut(x , y); break;
case 'm': scanf("%d%d%lf%lf" , &x , &f , &a1 , &a2) , x ++ , splay(x) , work(x , f , a1 , a2) , pushup(x); break;
default:
{
scanf("%d%d%lf" , &x , &y , &a1) , x ++ , y ++ ;
if(findroot(x) != findroot(y)) puts("unreachable");
else
{
for(i = 1 ; i < K ; i ++ ) p1[i] = p1[i - 1] * a1;
split(x , y) , ans = 0;
for(i = 0 ; i < K ; i ++ ) ans += p1[i] * a[y].sum[i];
printf("%.8le\n" , ans);
}
}
}
}
return 0;
}