• @loj



    @description@

    n 个点编号 0 到 n-1,每个点有一个从 [0,1] 映射到 [0,1] 的函数 f(x) 作为点权,它有以下几种形式:

    正弦函数:sin(ax+b) (a∈[0,1],b∈[0,π],a+b∈[0,π])
    指数函数:e^(ax+b) (a∈[−1,1],b∈[−2,0],a+b∈[−2,0])
    一次函数:ax+b (a∈[−1,1],b∈[0,1],a+b∈[0,1])

    有 m 个事件,种类如下:
    (1)加边。
    (2)删边。
    (3)更改点权。
    (4)询问 u 到 v 这条路径上的所有函数以 x(给定)为自变量的因变量之和。

    保证一开始不存在任何边。

    input
    第一行两个正整数 n,m 和一个字符串 type。表示 n 个点与 m 个事件,type 是用来得部分分的。1≤n≤100000,1≤m≤200000 。

    接下来 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)=e^(ax+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。

    output
    对于每个询问,输出一行实数,表示答案。建议使用科学计数法表示。

    sample input
    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

    sample output
    9.45520207e-001
    1.67942554e+000
    1.20000000e+000

    hint
    【出题人教你学数学】
    若函数 (f(x))(n) 阶导数在 ([a,b]) 区间内连续,则对 (f(x))(x_0(x_0in[a,b])) 处使用 n 次拉格朗日中值定理可以得到带拉格朗日余项的泰勒展开式:

    [f(x)=f(x_0)+frac{f'(x_0)(x-x_0)}{1!}+frac{f''(x_0)(x-x_0)^2}{2!}+ cdots +frac{f^{(n-1)}(x_0)(x-x_0)^{n-1}}{(n-1)!}+frac{f^{(n)}(xi)(x-x_0)^n}{n!},xin[a,b] ]

    其中,当 (x>x_0) 时,(xiin[x_0,x])。当 (x<x_0) 时,(xiin[x,x_0])

    (f^{(n)}) 表示函数 (f)(n) 阶导数

    @solution@

    都加边删边了,那肯定是用 LCT 来维护了。

    一次函数还好说,直接链上维护一次项系数与常数系数之和(但是竟然没有这部分的部分分?)。

    考虑指数函数或是三角函数的和,这个东西是没有任何实际的意义的,无法用什么统一的数据表示。

    但是题目中给的泰勒展开这玩意儿,其意义就是用多项式函数去逼近这些非多项式的函数。
    那我们为什么不直接用泰勒展开来逼近就可以了?取前二十项的,误差就足够小了。
    而多项式就可以直接用一次函数的方法,维护每一项的系数之和。

    那么怎么求导呢?显然我一个初中生是做不来的。

    直接摆公式吧:

    指数函数:(f(x) = e^x, f'(x) = e^x)
    三角函数 sin:(f(x) = sin(x), f'(x) = cos(x))。三角函数 cos:(f(x) = cos(x), f'(x) = -sin(x))
    幂函数: (f(x) = x^a, f'(x) = ax^{a-1})。常函数:(f(x) = k, f'(x) = 0)

    函数和的导数:(h(x) = f(x) + g(x), h'(x) = f'(x) + g'(x))
    函数积的导数:(h(x) = f(x)*g(x), h'(x) = f(x)*g'(x) + f'(x)*g(x))
    复合函数的导数:(h(x) = f(g(x)), h'(x) = f'(g(x))*g'(x))

    那么对于函数 (f(x) = e^{ax+b}),它的 n 阶导数为 (f^{(n)}=a^ne^{ax+b})
    对于函数 (f(x) = sin(x)),它的 n 阶导数的系数绝对值为 (a^n),剩下的以四为循环节:(sin(x))(cos(x))(-sin(x))(-cos(x))
    对于函数 (f(x) = ax + b),这个就不讲了。

    @accepted code@

    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef double db;
    const int MAXN = 100000;
    struct node{
    	int rev;
    	node *ch[2], *fa;
    	db k[20], s[20];
    }pl[MAXN + 5], *ad[MAXN + 5], *NIL;
    bool isroot(node *x) {return x->fa->ch[0] != x && x->fa->ch[1] != x;}
    void setchild(node *x, node *y, int d) {
    	if( x != NIL ) x->ch[d] = y;
    	if( y != NIL ) y->fa = x;
    }
    void pushup(node *x) {
    	for(int i=0;i<20;i++)
    		x->s[i] = x->ch[0]->s[i] + x->ch[1]->s[i] + x->k[i];
    }
    void pushdown(node *x) {
    	if( x->rev ) {
    		swap(x->ch[0], x->ch[1]);
    		if( x->ch[0] != NIL ) x->ch[0]->rev ^= 1;
    		if( x->ch[1] != NIL ) x->ch[1]->rev ^= 1;
    		x->rev = 0;
    	}
    }
    void rotate(node *x) {
    	node *y = x->fa; pushdown(y), pushdown(x);
    	int d = (y->ch[1] == x);
    	if( isroot(y) ) x->fa = y->fa;
    	else setchild(y->fa, x, (y->fa->ch[1] == y));
    	setchild(y, x->ch[!d], d);
    	setchild(x, y, !d);
    	pushup(y);
    }
    void splay(node *x) {
    	pushdown(x);
    	while( !isroot(x) ) {
    		node *y = x->fa;
    		if( isroot(y) )
    			rotate(x);
    		else {
    			if( (y->fa->ch[1] == y) == (y->ch[1] == x) )
    				rotate(y);
    			else rotate(x);
    			rotate(x);
    		}
    	}
    	pushup(x);
    }
    void access(node *x) {
    	node *y = NIL;
    	while( x != NIL ) {
    		splay(x);
    		x->ch[1] = y;
    		pushup(x);
    		y = x, x = x->fa;
    	}
    }
    void makeroot(node *x) {
    	access(x);splay(x);
    	x->rev ^= 1;
    }
    node *findroot(node *x) {
    	access(x), splay(x);
    	node *ret = x;
    	while( ret->ch[0] != NIL ) ret = ret->ch[0];
    	return ret;
    }
    void link(node *x, node *y) {
    	makeroot(x); x->fa = y;
    }
    void cut(node *x, node *y) {
    	makeroot(x); access(y), splay(y);
    	y->ch[0] = NIL; x->fa = NIL;
    	pushup(y);
    }
    void modify(db k[], int f, db a, db b) {
    	if( f == 1 ) {
    		db m = 1, x = sin(b), y = cos(b);
    		for(int i=0;i<20;i+=4) {
    			k[i + 0] = x*m, m = m*a/(i+1);
    			k[i + 1] = y*m, m = m*a/(i+2);
    			k[i + 2] = -x*m, m = m*a/(i+3);
    			k[i + 3] = -y*m, m = m*a/(i+4);
    		}
    	}
    	else if( f == 2 ) {
    		db m = 1, x = exp(b);
    		for(int i=0;i<20;i++)
    			k[i] = x*m, m = m*a/(i+1);
    	}
    	else if( f == 3 ) {
    		for(int i=0;i<20;i++)
    			k[i] = 0;
    		k[0] = b, k[1] = a;
    	}
    }
    db calculate(db k[], db x) {
    	db ret = 0, p = 1;
    	for(int i=0;i<20;i++)
    		ret += p*k[i], p *= x;
    	return ret;
    }
    void init() {NIL = &pl[0], NIL->ch[0] = NIL->ch[1] = NIL->fa = NIL;}
    char op[10];
    int main() {
    	int n, m; init();
    	scanf("%d%d%s", &n, &m, op);
    	for(int i=1;i<=n;i++) {
    		ad[i] = &pl[i], ad[i]->ch[0] = ad[i]->ch[1] = ad[i]->fa = NIL;
    		for(int j=0;j<20;j++)
    			ad[i]->k[j] = ad[i]->s[j] = 0;
    		int f; db a, b; scanf("%d%lf%lf", &f, &a, &b);
    		modify(ad[i]->k, f, a, b), pushup(ad[i]);
    	}
    	for(int i=1;i<=m;i++) {
    		scanf("%s", op);
    		if( op[0] == 'a' ) {
    			int u, v; scanf("%d%d", &u, &v); u++, v++;
    			link(ad[u], ad[v]);
    		}
    		if( op[0] == 'd' ) {
    			int u, v; scanf("%d%d", &u, &v); u++, v++;
    			cut(ad[u], ad[v]);
    		}
    		if( op[0] == 'm' ) {
    			int c, f; db a, b; scanf("%d%d%lf%lf", &c, &f, &a, &b); c++;
    			access(ad[c]), splay(ad[c]), modify(ad[c]->k, f, a, b), pushup(ad[c]);
    		}
    		if( op[0] == 't' ) {
    			int u, v; db x; scanf("%d%d%lf", &u, &v, &x); u++, v++;
    			makeroot(ad[u]);
    			if( findroot(ad[v]) != ad[u] ) puts("unreachable");
    			else {
    				db ans = calculate(ad[v]->s, x); int k = 0;
    				if( ans < 1 ) {
    					while( ans < 1 ) ans *= 10, k++;
    					printf("%0.8lfe-%03d
    ", ans, k);
    				}
    				else {
    					while( ans >= 10 ) ans /= 10, k++;
    					printf("%0.8lfe+%03d
    ", ans, k);
    				}
    			}
    		}
    	}
    }
    

    @details@

    想到我考前还没写过 LCT。
    想到我还要这道题没做。
    于是就有了这篇博客。

    另外,虽然题目是建议科学计数法,但是如果不用科学计数法就会因为浮点误差 WA 掉。
    原因?你想啊,假如一个五位数从 10^(-6) 开始出现误差,你把它变成科学计数法,它在 10^(-7) 之前不就都没有误差了吗?

  • 相关阅读:
    js堆和栈
    document.write的用处!
    nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模块的Javascript
    addEventListener和attachEvent的区别
    保留两位小数
    文字图片飞舞
    CSS3 2D 转换
    安装phpstudy之后发现80端口被占用
    docker 部署项目的实战操作
    性能实战第一天基础01-设计测试场景以及如何做性能测试
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10289348.html
Copyright © 2020-2023  润新知