zoukankan      html  css  js  c++  java
  • 题解-CF232C Doe Graphs

    题面

    CF232C Doe Graphs

    (0)Doe 图是一个点,(1)Doe 图是两个点加一条边,(k)Doe 图是一个 (k-1)Doe 图加上一个编号全加了 (k-1)Doe 图大小的 (k-2)Doe 图。给一个 (n)Doe 图,(m) 次询问,每次查询 (a)(b) 两点间的最短路。

    数据范围:(1le nle 10^3)(1le mle 10^5)(1le a,ble 10^{16})


    题解

    肝了一个下午最终贺题了。既然狗做不动题,就多写点题解吧。

    注意到 (a,b)(fib_n) 小很多,发现 (n)(fib_{79})(>10^{16})) 可以和 (80) 取最小值。

    考虑一个 (k) 级图,把 (k-1) 级部分叫做 (A),把 (k-2) 级部分叫做 (B)

    有四个点:(S=1)(T_A=fib_{k-1})(S_B=T_A+1)(T=fib_{k})

    有两条边在这里:((S,S_B))((T_A,S_B))


    容易想到计算出每个点到 (S) 的距离和到 (T) 的距离,然后任意两点间的询问,貌似可以通过这两个距离搞出来。

    这样便缩小了查询的一维。但是还是不能直接求。

    由于 (a,b) 是固定的,所以它们在每级图中的地位也是固定的,所以可以考虑只算对于一个查询的点 (u),计算它在每层图中和 (S,T) 的距离,这东西是可以递推的。

    如果设 (f(k,0/1)) 表示 (k) 级图下 (u)(S/T) 的距离,便可以非常睿智地玩出一个递推方程,但它是错的。

    玩的时候,会发现一个 (k) 级图外的边也是对内部的距离有影响的。

    最好的例子就是对于 (S)(T_A),它们都连向 (S_B),而它们的内部距离可能 (>2)

    然后狗就是很 naive 以为只要对这种情况特殊处理掉就 OK 了,没想到有以下情况:

    有以下边:((S,T))((T_A,S_B))((S,S_B)),然后查询 (S_B)(T) 的距离。

    这种时其实是有个性质的:所有的外部边的影响可以改为在 (S,T) 之间加一条带权边。

    可以把 dp 转成搜索,(dp(u,k,d)) 表示 (k) 级图,(S,T) 上有一条长度 (k) 的边,返回值是个 pair,即 (u)(S,T) 的距离。

    然后就很好递推了。


    再考虑接下来如何求 (a,b) 之间的距离,不妨设 (a<b)

    发现也不能直接把 (n) 替换成最小的 (k) 使得 (ble fib_k),原理同上:有外部影响。

    但是推推可以发现:虽然整个图对答案有影响,但因为如果对于最小 (k) 满足 (ble fib_{k-1})(a,b) 必然都在它大的部分子图内,所以这里的 (d) 肯定是 (2),所以 (n)(80)min 依然是可以的 /cy

    所以同样可以搜索,设 (solve(u,v,k,d)) 表示 (k) 级图,(S,T) 上有一条长度 (k) 的边,返回它们间的距离。


    至于 (d) 怎么递推,dp 怎么递推(包括上面)留给读者自行思考了。

    时间复杂度 (Theta(80m))


    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef double db;
    #define x first
    #define y second
    #define bg begin()
    #define ed end()
    #define pb push_back
    #define mp make_pair
    #define sz(u) int((u).size())
    #define R(i,n) for(int i(0);i<(n);++i)
    #define L(i,n) for(int i((n)-1);i>=0;--i)
    const int iinf=0x3f3f3f3f;
    const ll linf=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=80;
    int n;
    
    //Math
    ll fib[N];
    void math_init(){
        fib[0]=fib[1]=1;
        for(int i=2;i<N;++i)    
            fib[i]=fib[i-1]+fib[i-2];
    }
    
    //Function
    #define mi fib[k-1]
    pair<int,int> dp(ll u,int k,int d=iinf){
        if(k<=1) return mp(0,0);
        if(u<mi){
            auto p=dp(u,k-1,2);
            return mp(p.x,min(min(p.x,p.y)+1+(k-2)/2,p.x+d));
        } else {
            auto p=dp(u-mi,k-2,d+1);
            return mp(min(p.x+1,p.y+d),p.y);
        }
    }
    int solve(ll u,ll v,int k,int d=iinf){
        if(u==0&&v==fib[k]-1) return min(d,k/2);
        if(v<mi) return solve(u,v,k-1,2);
        if(u>=mi) return solve(u-mi,v-mi,k-2,d+1);
        auto ud=dp(u,k-1,2),vd=dp(v-mi,k-2,d+1);
        return min(min(ud.x,ud.y)+1+vd.x,ud.x+d+vd.y);
    }
    
    //Main
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0),cout.tie(0);
        math_init();
        int m; cin>>m>>n,n=min(n+1,N-1);
        while(m--){
            ll u,v; cin>>u>>v,--u,--v;
            if(u>v) swap(u,v);
            cout<<solve(u,v,n)<<'
    ';
        }
        return 0;
    }
    
    

    祝大家学习愉快!

  • 相关阅读:
    一道网易面试题
    OC的引用计数
    ReplayKit2 采集音视频回调格式分析
    《剑指offer3- 从末尾到头打印链表》
    《剑指offer
    《剑指offer
    ReplayKit2:声音回调时间戳问题
    UILable在Autolayout模式下面自动调节字体大小
    建表手写语句
    oracle创建主键序列和在ibatis中应用
  • 原文地址:https://www.cnblogs.com/George1123/p/14231560.html
Copyright © 2011-2022 走看看