bzoj 5020(洛谷4546) [THUWC 2017]在美妙的数学王国中畅游——LCT+泰勒展开

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5020

   https://www.luogu.org/problemnew/show/P4546

如果保证 x=1 ,则可以用 LCT 维护每个点的函数值。不然的话就用 LCT 拿出那条链,dfs 一下 splay 现算。可以得 60 分。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define db double
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
const int N=1e5+5;
int n,m,fa[N],c[N][2],sta[N],top; db sm[N],vl[N]; bool fx,rev[N];
struct Node{
  int f;db a,b;
  db cal(db x)
  {
    if(f==1)return sin(a*x+b);
    if(f==2)return exp(a*x+b);
    if(f==3)return a*x+b;
  }
}a[N];
bool isroot(int cr){return c[fa[cr]][0]!=cr&&c[fa[cr]][1]!=cr;}
void pshp(int cr){sm[cr]=sm[c[cr][0]]+sm[c[cr][1]]+vl[cr];}
void Rev(int cr){if(rev[cr]){rev[cr]=0;rev[c[cr][0]]^=1;rev[c[cr][1]]^=1;swap(c[cr][0],c[cr][1]);}}
void rotate(int x)
{
  int y=fa[x],z=fa[y],d=(x==c[y][1]);
  if(!isroot(y))c[z][y==c[z][1]]=x;//!isroot(y) not !isroot(z) !!
  fa[x]=z;
  fa[y]=x; fa[c[x][!d]]=y;
  c[y][d]=c[x][!d]; c[x][!d]=y;
  if(fx)pshp(y),pshp(x);
}
void splay(int x)
{
  sta[top=1]=x;
  for(int cr=x;!isroot(cr);cr=fa[cr])sta[++top]=fa[cr];
  for(int i=top;i;i--)Rev(sta[i]);
  int y,z;
  while(!isroot(x))
    {
      y=fa[x];z=fa[y];
      if(!isroot(y))
    ((x==c[y][0])^(y==c[z][0]))?rotate(x):rotate(y);
      rotate(x);
    }
}
void access(int cr)
{
  for(int t=0;cr;splay(cr),c[cr][1]=t,pshp(cr),t=cr,cr=fa[cr]);//pshp()!!
}
void mkrt(int cr)
{
  access(cr);splay(cr);rev[cr]^=1;
}
void link(int x,int y)
{
  mkrt(x);fa[x]=y;
}
void cut(int x,int y)
{
  mkrt(x);access(y);splay(y);
  fa[x]=c[y][0]=0;if(fx)sm[y]-=vl[x];
}
void mdfy(int cr,int f,db a1,db b1)
{
  if(fx)splay(cr),sm[cr]-=vl[cr];
  a[cr].f=f;a[cr].a=a1;a[cr].b=b1;
  if(fx)vl[cr]=a[cr].cal(1),sm[cr]+=vl[cr];
}
int fnd(int cr)//root of real tree
{
  while(!isroot(cr))cr=fa[cr];return cr;
}
db dfs(int cr,db x)
{
  db ret=a[cr].cal(x);
  if(c[cr][0])ret+=dfs(c[cr][0],x);
  if(c[cr][1])ret+=dfs(c[cr][1],x);
  return ret;
}
void print(db x)
{
  bool fx=0;int t=0;
  if(x<1){fx=1;while(x<1)x*=10,t++;}
  if(x>=10){while(x>=10)x/=10,t++;}
  printf("%.8fe%c%03d\n",x,fx?'-':'+',t);
}
int main()
{
  n=rdn();m=rdn();char ch[15];scanf("%s",ch);
  if(ch[1]=='0')fx=1;
  for(int i=1;i<=n;i++)
    {
      a[i].f=rdn();scanf("%lf%lf",&a[i].a,&a[i].b);
      if(fx)vl[i]=sm[i]=a[i].cal(1);
    }
  int u,v;db a,b,x;
  while(m--)
    {
      scanf("%s",ch);
      if(ch[0]=='a'){u=rdn()+1;v=rdn()+1;link(u,v);}
      if(ch[0]=='d'){u=rdn()+1;v=rdn()+1;cut(u,v);}
      if(ch[0]=='m'){u=rdn()+1;v=rdn();scanf("%lf%lf",&a,&b);mdfy(u,v,a,b);}
      if(ch[0]=='t')
    {
      u=rdn()+1;v=rdn()+1;scanf("%lf",&x);
      mkrt(u);access(v);splay(v);
      if(fnd(u)!=v)puts("unreachable");
      else print(fx?sm[v]:dfs(v,x));
    }
    }
  return 0;
}
View Code

关于本题的求导和泰勒展开可以看这个:https://www.cnblogs.com/zhoushuyu/p/8148732.html

似乎还可以麦克劳林展开:https://www.cnblogs.com/Troywar/p/8982707.html

似乎展开成 12 项的多项式就差不多了,然后可以用 LCT 维护 12 项的系数和,算的时候乘上 xk 再除以阶乘就行。

那个 x0 好像取 0 就行了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define db double
#define ls c[cr][0]
#define rs c[cr][1]
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
using namespace std;
const int N=1e5+5,M=12;
int n,fa[N],c[N][2],sta[N],top; bool rev[N]; db sm[N][M+5],vl[N][M+5],jc[N];
struct Node{
  int f;db a,b;
  Node(int f=0,db a=0,db b=0):f(f),a(a),b(b) {}
  void cz(int cr)
  {
    if(f==1)
      {
    db p[5],lja=1;
    p[0]=sin(b);p[1]=cos(b);p[2]=-p[0];p[3]=-p[1];
    for(int i=0;i<M;i+=4)
      for(int j=0;j<4;j++,lja*=a)sm[cr][i+j]=vl[cr][i+j]=p[j]*lja;
      }
    if(f==2)
      {
    db ml=exp(b),lja=1;
    for(int i=0;i<M;i++,lja*=a)sm[cr][i]=vl[cr][i]=ml*lja;
      }
    if(f==3)
      {
    sm[cr][0]=vl[cr][0]=b; sm[cr][1]=vl[cr][1]=a;
    for(int i=2;i<M;i++)sm[cr][i]=vl[cr][i]=0;
      }
  }
}w[N];
bool isrt(int cr){return c[fa[cr]][0]!=cr&&c[fa[cr]][1]!=cr;}
void pshp(int cr)
{
  for(int i=0;i<M;i++)sm[cr][i]=sm[ls][i]+sm[rs][i]+vl[cr][i];
}
void Rev(int cr){if(rev[cr]){rev[cr]=0;rev[ls]^=1;rev[rs]^=1;swap(ls,rs);}}
void rotate(int x)
{
  int y=fa[x],z=fa[y],d=(x==c[y][1]);
  if(!isrt(y))c[z][y==c[z][1]]=x;
  fa[x]=z;
  fa[y]=x; fa[c[x][!d]]=y;
  c[y][d]=c[x][!d]; c[x][!d]=y;
  pshp(y);pshp(x);
}
void splay(int x)
{
  sta[top=1]=x;
  for(int cr=x;!isrt(cr);cr=fa[cr])sta[++top]=fa[cr];
  for(int i=top;i;i--)Rev(sta[i]);
  int y,z;
  while(!isrt(x))
    {
      y=fa[x];z=fa[y];
      if(!isrt(y))
    ((x==c[y][0])^(y==c[z][0]))?rotate(x):rotate(y);
      rotate(x);
    }
}
void access(int cr)
{
  for(int t=0;cr;splay(cr),rs=t,pshp(cr),t=cr,cr=fa[cr]);//pshp()!!!
}
void mkrt(int cr)
{
  access(cr);splay(cr);rev[cr]^=1;
}
void link(int x,int y)
{
  mkrt(x);fa[x]=y;
}
void cut(int x,int y)
{
  mkrt(x);access(y);splay(y);
  fa[x]=c[y][0]=0;pshp(y);
}
void mdfy(int cr,int f,db a,db b)
{
  splay(cr);
  w[cr]=Node(f,a,b);w[cr].cz(cr);
}
db cal(int cr,db x)
{
  db ret=0,ml=1;
  for(int i=0;i<M;i++,ml*=x)
    ret+=sm[cr][i]*ml/jc[i];
  return ret;
}
int fnd(int cr)
{
  while(!isrt(cr))cr=fa[cr];return cr;
}
void print(db x)
{
  bool fx=0;int t=0;
  if(x<1){fx=1;while(x<1)x*=10,t++;}
  if(x>=10){while(x>=10)x/=10,t++;}
  printf("%.8fe%c%03d\n",x,fx?'-':'+',t);
}
int main()
{
  jc[0]=1;for(int i=1;i<M;i++)jc[i]=jc[i-1]*i;
  n=rdn();int m=rdn();char ch[15];scanf("%s",ch);
  for(int i=1;i<=n;i++)
    w[i].f=rdn(),scanf("%lf%lf",&w[i].a,&w[i].b),w[i].cz(i),pshp(i);//pshp for sm[]
  int u,v;db a,b;
  while(m--)
    {
      scanf("%s",ch);
      if(ch[0]=='a'){u=rdn()+1;v=rdn()+1;link(u,v);}
      if(ch[0]=='d'){u=rdn()+1;v=rdn()+1;cut(u,v);}
      if(ch[0]=='m'){u=rdn()+1;v=rdn();scanf("%lf%lf",&a,&b);mdfy(u,v,a,b);}
      if(ch[0]=='t')
    {
      u=rdn()+1;v=rdn()+1;scanf("%lf",&a);
      mkrt(u);access(v);splay(v);
      if(fnd(u)!=v)puts("unreachable");
      else print(cal(v,a));
    }
    }
  return 0;
}

 

转载于:https://www.cnblogs.com/Narh/p/10253975.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值