题目链接
一个森林,支持加边删边
每一个点上有一个关于x的函数,函数值在[0,1]内
每次给出一个x并询问一条路径上所有点的函数值的和,x也是[0,1]
所有人智商为1时,直接算函数值并用LCT维护路径即可
如果只有一次函数就维护 ∑a ∑ a 和 ∑b ∑ b
然后就是怎么搞这两个不是多项式的函数了
为什么不好处理,因为我们不能通过x的次数相同的把和x有关的东西提取出来预先算好
于是恶补一发数学
可以把一个非多项式函数在一个x=x0的地方把它展开成多项式函数
若函数 f(x) f ( x ) 的 n n 阶导数在 [a,b] [ a , b ] 区间内连续,则对 f(x) f ( x ) 在 x=x0(x0∈[a,b]) x = x 0 ( x 0 ∈ [ a , b ] ) 处使用 n n 次拉格朗日中值定理可以得到带拉格朗日余项的泰勒展开式
泰勒展开式:
既然x的范围是 [a,b] [ a , b ] ,且三个函数都可导,那么显然取 x0=0 x 0 = 0
这样我们就可以愉快地合并每一个函数相同次数的a和b了,用LCT维护和即可
最后统计答案的时候每一个项乘上x的对应次数再除以阶乘就可以了,求导的次数大概14左右就行了
我们是不是还漏了点东西
不会求导怎么办?GG
要用到的基本的求导公式:
于是题目中的三个函数就是两个复合函数,一个不需要求导就能做的一次函数
泰勒展开式+LCT乱搞就行了(OWO)
代码:
#include
#include
#include
#include
#include
#include
#include
#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;iif(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;isum[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;isum[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;i1]*(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;isum[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;
}