[BZOJ3528][Zjoi2014][数学]星系调查

上一发大神的推导
这里写图片描述
这里写图片描述

后面代入求根公式就行。
因为数据只有M=N或M=N-1两种情况,所以这张图要不是一棵树要不只有一个环,是一棵树的情况很好考虑,有一个环的话要分类讨论。

感觉ZJOI2014DAY1的题代码都好长,然而杜教AK了…….

感觉这张图上一坨水印看着不爽,不知道怎么去……

#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <iostream>
#define N 100010

using namespace std;

typedef long long ll;

int n,m,q,cnt,u,v,l,Root,ccnt;
int G[N],X[N],Y[N],dpt[N],sta[N],tp,V[N],inC[N],onT[N],r[N];
int fa[N][30];
ll sgx[N],sgy[N],sgx_2[N],sgy_2[N],sgxy[N];
ll csgx,csgy,csgx_2,csgy_2,csgxy;
double A,B,C;

struct edge{
    int t,nx;
}E[N<<2];

inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p1==p2){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2) return EOF;
    }
    return *p1++;
}

inline void reaD(int &x){
    char Ch=gc();x=0;
    for(;Ch>'9'||Ch<'0';Ch=gc());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=gc());
}

inline void InserT(int x,int y){
    E[++cnt].t=y;E[cnt].nx=G[x];G[x]=cnt;
    E[++cnt].t=x;E[cnt].nx=G[y];G[y]=cnt;
}

void dfs_t(int x,int f,int flg=0){
    onT[x]=flg;
    dpt[x]=dpt[f]+1;fa[x][0]=f;
    sgxy[x]=sgxy[f]+X[x]*Y[x];
    sgx[x]=sgx[f]+X[x],sgy[x]=sgy[f]+Y[x];
    sgx_2[x]=sgx_2[f]+X[x]*X[x],sgy_2[x]=sgy_2[f]+Y[x]*Y[x];
    for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f&&!inC[E[i].t]) dfs_t(E[i].t,x,flg);
}

void dfs_g(int x,int f){
    V[x]=1;
    dpt[x]=dpt[f]+1;
    sgxy[x]=sgxy[f]+X[x]*Y[x];
    sgx[x]=sgx[f]+X[x],sgy[x]=sgy[f]+Y[x];
    sgx_2[x]=sgx_2[f]+X[x]*X[x],sgy_2[x]=sgy_2[f]+Y[x]*Y[x];
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f&&inC[E[i].t]&&!V[E[i].t]) dfs_g(E[i].t,x);
}

inline void swap(int &x,int &y){
    int z=x;x=y;y=z;
}

int lca(int x,int y){
    if(dpt[x]<dpt[y]) swap(x,y);
    for(int i=20;~i;i--)
        if(dpt[fa[x][i]]>=dpt[y]) x=fa[x][i];
    if(x==y) return x;
    for(int i=20;~i;i--)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void Calc_t(int u,int v){
    l=lca(u,v);
    ll nn=dpt[u]+dpt[v]-2*dpt[l]+1,_sgx=sgx[u]+sgx[v]-2*sgx[l]+X[l],_sgy=sgy[u]+sgy[v]-2*sgy[l]+Y[l],
       _sgx_2=sgx_2[u]+sgx_2[v]-2*sgx_2[l]+X[l]*X[l],_sgy_2=sgy_2[u]+sgy_2[v]-2*sgy_2[l]+Y[l]*Y[l],_sgxy=sgxy[u]+sgxy[v]-2*sgxy[l]+Y[l]*X[l];
    double _x=(double)_sgy/nn,_y=(double)_sgx/nn;
    A=nn*_y*_y-2*_y*_sgx+_sgx_2;
    B=2*_x*_sgx-2*nn*_x*_y+2*_y*_sgy-2*_sgxy;
    C=nn*_x*_x-2*_x*_sgy+_sgy_2;
    printf("%.5lf\n",(A+C-sqrt((A-C)*(A-C)+B*B))/2.0+1e-7);
}

void Calc_c(int u,int v){
    if(dpt[u]<dpt[v]) swap(u,v);
    ll nn=dpt[u]-dpt[v]+1,_sgx=sgx[u]-sgx[v]+X[v],_sgy=sgy[u]-sgy[v]+Y[v],
       _sgx_2=sgx_2[u]-sgx_2[v]+X[v]*X[v],_sgy_2=sgy_2[u]-sgy_2[v]+Y[v]*Y[v],_sgxy=sgxy[u]-sgxy[v]+Y[v]*X[v];
    double _x=(double)_sgy/nn,_y=(double)_sgx/nn,Ans;
    A=nn*_y*_y-2*_y*_sgx+_sgx_2;
    B=2*_x*_sgx-2*nn*_x*_y+2*_y*_sgy-2*_sgxy;
    C=nn*_x*_x-2*_x*_sgy+_sgy_2;
    Ans=(A+C-sqrt((A-C)*(A-C)+B*B))/2.0;
    nn=ccnt-nn+2,_sgx=csgx-_sgx+X[u]+X[v],_sgy=csgy-_sgy+Y[u]+Y[v],_sgx_2=csgx_2-_sgx_2+X[u]*X[u]+X[v]*X[v],_sgy_2=csgy_2-_sgy_2+Y[u]*Y[u]+Y[v]*Y[v];_sgxy=csgxy-_sgxy+X[u]*Y[u]+X[v]*Y[v];
    _x=(double)_sgy/nn,_y=(double)_sgx/nn,Ans;
    A=nn*_y*_y-2*_y*_sgx+_sgx_2;
    B=2*_x*_sgx-2*nn*_x*_y+2*_y*_sgy-2*_sgxy;
    C=nn*_x*_x-2*_x*_sgy+_sgy_2;
    printf("%.5lf\n",min((A+C-sqrt((A-C)*(A-C)+B*B))/2.0,Ans)+1e-7);
}

void Calc_t_c(int u,int v){
    ll n=0,sgx1=0,sgy1=0,sgx21=0,sgy21=0,sgxy1=0;
    if(onT[u]) n=dpt[u],sgx1=sgx[u],sgy1=sgy[u],sgx21=sgx_2[u],sgy21=sgy_2[u],sgxy1=sgxy[u],u=r[onT[u]];
    if(onT[v]) n+=dpt[v],sgx1+=sgx[v],sgy1+=sgy[v],sgx21+=sgx_2[v],sgy21+=sgy_2[v],sgxy1+=sgxy[v],v=r[onT[v]];

    if(dpt[u]<dpt[v]) swap(u,v);
    ll nn=dpt[u]-dpt[v]+1,_sgx=sgx[u]-sgx[v]+X[v],_sgy=sgy[u]-sgy[v]+Y[v],
       _sgx_2=sgx_2[u]-sgx_2[v]+X[v]*X[v],_sgy_2=sgy_2[u]-sgy_2[v]+Y[v]*Y[v],_sgxy=sgxy[u]-sgxy[v]+Y[v]*X[v];
    double _x=(double)(_sgy+sgy1)/(nn+n),_y=(double)(_sgx+sgx1)/(nn+n),Ans;
    A=(nn+n)*_y*_y-2*_y*(_sgx+sgx1)+(_sgx_2+sgx21);
    B=2*_x*(_sgx+sgx1)-2*(nn+n)*_x*_y+2*_y*(_sgy+sgy1)-2*(_sgxy+sgxy1);
    C=(nn+n)*_x*_x-2*_x*(_sgy+sgy1)+(_sgy_2+sgy21);
    Ans=(A+C-sqrt((A-C)*(A-C)+B*B))/2.0;
    nn=ccnt-nn+2,_sgx=csgx-_sgx+X[u]+X[v],_sgy=csgy-_sgy+Y[u]+Y[v],_sgx_2=csgx_2-_sgx_2+X[u]*X[u]+X[v]*X[v],_sgy_2=csgy_2-_sgy_2+Y[u]*Y[u]+Y[v]*Y[v];_sgxy=csgxy-_sgxy+X[u]*Y[u]+X[v]*Y[v];
    _x=(double)(_sgy+sgy1)/(nn+n),_y=(double)(_sgx+sgx1)/(nn+n);
    A=(nn+n)*_y*_y-2*_y*(_sgx+sgx1)+(_sgx_2+sgx21);
    B=2*_x*(_sgx+sgx1)-2*(nn+n)*_x*_y+2*_y*(_sgy+sgy1)-2*(_sgxy+sgxy1);
    C=(nn+n)*_x*_x-2*_x*(_sgy+sgy1)+(_sgy_2+sgy21);
    printf("%.5lf\n",min((A+C-sqrt((A-C)*(A-C)+B*B))/2.0,Ans)+1e-7);
}

void WorkOnTree(){
    dfs_t(1,0);
    for(reaD(q);q;q--)
        reaD(u),reaD(v),Calc_t(u,v);
}

bool Getcir(int x,int f){
    if(V[x]){
        while(tp){
            int k=sta[tp--];inC[k]=1;ccnt++;
            csgx+=X[k],csgy+=Y[k],csgx_2+=X[k]*X[k];
            csgy_2+=Y[k]*Y[k],csgxy+=X[k]*Y[k];
            if(k==x) break;
        }
        return true;
    }
    V[x]=1;
    sta[++tp]=x;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f){
            if(Getcir(E[i].t,x))return true;
        }else f=0;
    tp--;
    return false;
}

void WorkOnGraph(){
    Getcir(1,0);cnt=0;
    for(int k=1;k<=n;k++)
        if(inC[k])for(int i=G[k];i;i=E[i].nx)
            if(!inC[E[i].t]) dfs_t(E[i].t,0,E[i].t),r[E[i].t]=k;
    memset(V,0,sizeof(V));
    for(int k=1;k<=n;k++)
        if(inC[k]){dfs_g(k,0);Root=k;break;}
    for(reaD(q);q;q--){
        reaD(v);reaD(u);
        if(onT[v]==onT[u]&&!inC[v]&&!inC[u]) Calc_t(u,v);
        else if(inC[v]&&inC[u]) Calc_c(u,v);
        else Calc_t_c(u,v);
    }
}

int main(){
    reaD(n);reaD(m);
    for(int i=1;i<=n;i++) reaD(X[i]),reaD(Y[i]);
    for(int i=1;i<=m;i++) reaD(u),reaD(v),InserT(u,v);
    if(m==n-1) WorkOnTree();
    else WorkOnGraph();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一一个整数 $n$。 接下来 $n$ ,每 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时复杂度 树形dp的时复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时复杂度 动态规划的时复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值