• 题解 P5037 【抓捕】


    照例先扯淡

    不知道大家有没有做过P4473 [国家集训队]飞飞侠呢?如果没做过可以去看一下,那题的这道题就是用了飞飞侠的优化思想。有一次考试,我们考到了飞飞侠这道题,然后标程是分层图,由于我太菜了,没听懂就上网搜讲解,结果发现了一个神仙算法:Dijkstra+并查集优化(后来zcysky大佬也在洛谷上发了相关的题解,我也很不要脸的发了一篇,此处就不讲了),在看懂之后我就把那题的优化思路运用到了这里QAQ。

    扯完了,下面开始讲题目。

    先把题意简化一下:给你编号为$1$到$n$的$n$个点,两个编号互质的点可以连边,从相同的点出发的边权值相同,求给定两点之间最短路(其实除了$1$号点,其余每个点都是相互连通的,按照惯例输出$-1$的数据是不存在的)。

    题目大致分成$2$部分:1.枚举$n$以内的互质点对;2.求最短路

    对于第一部分,我本来是想卡一下暴力枚举,只让构造法过的,但是由于$n$太小,所以构造甚至跑的比暴力枚举还慢QAQ(难道是我写的太垃圾了???),所以......最后并没有卡。

    但是还是讲一下构造法的思路吧(大家看看就好):

    1.首先预处理$1$~$n$中所有的质数

    2.枚举$i=1$~$n$

    3.对于每一个$i$,分解质因数,求出它使用了那些质数

    4.从$i$没有使用过的质数中选取一些进行组合并使其小于$i$

    代码如下(最短路部分没有优化):

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f
    using namespace std;
    const int maxn = 105;
    ll x[50010],Ans1,Su[10010],Fa[10010],Cnt,Ans,n,X,Dist[10010],w[10010];
    bool Pd[10010],Used[10010],From[10010][10010];
    int T,u,v,Head[10010],To[61000010],Next[61000010];
    queue<ll> Q;
    struct Node {
        int p,Dis;
    } Now,N;
    bool operator <(Node x,Node y) {
        return x.Dis>y.Dis;
    }
    priority_queue<Node> PQ;
    template <typename T>
    inline int Read(T &x) {
        x=0;
        int f=1;
        char c=getchar();
        while(c!='-'&&c>'9'&&c<'0')
            c=getchar();
        for(; !isdigit(c); c=getchar())
            if(c=='-')
                f=-f;
        for(; isdigit(c); c=getchar())
            x=x*10+c-'0';
        x*=f;
        if(c=='
    ')
            return 1;
        else
            return 0;
    }
    inline void Write(long long x) {
        if(x<0) {
            putchar('-');
            x=-x;
        }
        if(x>9) {
            Write((x-x%10)/10);
        }
        putchar(x%10+'0');
    }
    inline ll Multi(ll a, ll b, ll p) {
        ll Ans1 = 0;
        while(b) {
            if(b & 1LL)	Ans1 = (Ans1+a)%p;
            a = (a+a)%p;
            b >>= 1;
        }
        return Ans1;
    }
    inline ll QPow(ll a, ll b, ll p) {
        ll Ans1 = 1;
        while(b) {
            if(b & 1LL)	Ans1 = Multi(Ans1, a, p);
            a = Multi(a, a, p);
            b >>= 1;
        }
        return Ans1;
    }
    inline bool MR(ll n) {
        if(n == 2)	return true;
        int s = 20, i, t = 0;
        ll u = n-1;
        while(!(u&1)) {
            t++;
            u >>= 1;
        }
        while(s--) {
            ll a = rand()%(n-2)+2;
            x[0] = QPow(a, u, n);
            for(i = 1; i <= t; i++) {
                x[i] = Multi(x[i-1], x[i-1], n);
                if(x[i] == 1 && x[i-1] != 1 && x[i-1] != n-1)	return false;
            }
            if(x[t] != 1)	return false;
        }
        return true;
    }
    inline ll Gcd(ll a, ll b) {
        if(b == 0)
            return a;
        else
            return Gcd(b, a%b);
    }
    inline ll Pollard_Rho(ll n, int c) {
        ll i = 1, k = 2, x = rand()%(n-1)+1, y = x;
        while(1) {
            i++;
            x = (Multi(x, x, n)+c)%n;
            ll p = Gcd((y-x+n)%n, n);
            if(p != 1 && p != n)	return p;
            if(y == x)	return n;
            if(i == k) {
                y = x;
                k <<= 1;
            }
        }
    }
    inline void Find(ll x, int c) {
        if(x == 1)	return;
        if(MR(x)) {
            Pd[x]=1;
            return;
        }
        ll p = x, k = c;
        while(p >= x) {
            p = Pollard_Rho(p, c--);
        }
        Find(p, k);
        Find(x/p, k);
    }
    inline void Add(int a,int b) {
        To[++Cnt]=a;
        Next[Cnt]=Head[b];
        Head[b]=Cnt;
        To[++Cnt]=b;
        Next[Cnt]=Head[a];
        Head[a]=Cnt;
    }
    void Dijkstra(int x) {
        Dist[x]=0;
        Now.Dis=0;
        Now.p=x;
        PQ.push(Now);
        while(!PQ.empty()) {
            Now=PQ.top();
            From[Now.p][0]=0;
            PQ.pop();
            if(!Pd[Now.p]) {
                Pd[Now.p]=1;
                for(int i=Head[Now.p]; i; i=Next[i]) {
                    if(!Pd[To[i]]&&(!From[Now.p][Fa[To[i]]])&&(Dist[To[i]]>Dist[Now.p]+w[Now.p])) {
                        Fa[To[i]]=Now.p;
                        Dist[To[i]]=Dist[Now.p]+w[Now.p];
                        From[To[i]][Now.p]=1;
                        N.p=To[i];
                        N.Dis=Dist[Now.p]+w[Now.p];
                        PQ.push(N);
                    }
                }
            }
        }
    }
    int main() {
        Read(T);
        Read(n);
        Cnt=0;
        for(int i=2; i<=5000; i++) {
            if(MR(i)) {
                Su[++Cnt]=i;
            }
        }
        Cnt=0;
        for(int i=1; i<=n; i++) {
            memset(Pd,0,sizeof(Pd));
            memset(Used,0,sizeof(Used));
            Find(i,57);
            for(int j=1; Su[j]<i; j++) {
                if(!Pd[Su[j]]) {
                    Q.push(Su[j]);
                    Ans++;
                }
            }
            while(!Q.empty()) {
                X=Q.front();
                Add(X,i);
                Q.pop();
                for(int j=1; Su[j]<=i; j++) {
                    if(X*Su[j]<i&&(!Pd[Su[j]])&&(!Used[X*Su[j]])) {
                        Q.push(X*Su[j]);
                        Used[X*Su[j]]=1;
                        Ans++;
                    }
                }
            }
        }
        while(T--) {
            fill(Dist,Dist+n+5,INF);
            memset(Fa,0,sizeof(Fa));
            memset(From,0,sizeof(From));
            memset(Pd,0,sizeof(Pd));
            Read(u);
            Read(v);
            for(int i=1; i<=n; i++) {
                Read(w[i]);
            }
            Dijkstra(u);
            Write(Dist[v]);
            putchar('
    ');
        }
        return 0;
    }
    

    对于第二部分,相信大家一定都会写Dijkstra+堆优化,那么你就可以轻易得到50分了。

    那剩下的50分呢?

    其实仔细观察题目我们就会发现,从同一个点出发的边权值都相同,那么我们在把点压进优先队列时可以直接加上当前点的点权(即从当前点出发的边的权值),由于优先队列的性质,每次从优先队列中取出的点,其状态一定是最优解,而且其拓展出的状态也是最优解。证明如下:

    假设点$x$已经得到了最短路,证明用该点更新的$y$也得到了最短路

    反证,假设存在路径$x$′→$y$

    使$Dist[y]$ 更小,且在$x$更新$y$之后,那么有$Dist[x$′$]+w[x]<Dist[x]+w[x]$,因为$x$′在$x$之后,有$Dist[x$′$]+w[x$′$]≥Dist[x]+w[x]$,两式矛盾,运用数学归纳法,可知上述结论成立,以及起点$s$到每一点的最短路径就是$Dist[i]$

    其实和飞飞侠的证明是一样的(所以我就直接搬运了2333)。

    这样的话,每个点第一次被访问到就一定是最优解,所以只要找到需要的点就可以直接退出了。

    代码如下:

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f
    using namespace std;
    const int maxn = 105;
    ll x[50010],Ans1,Su[5010],Cnt,Ans,n,X,Dist[5010],w[5010],Da;
    bool Pd[5010],Used[5010];
    int T,u,v,Head[5010],To[15000010],Next[15000010];
    queue<ll> Q;
    struct Node {
        int p,Dis;
    } Now,N;
    bool operator <(Node x,Node y) {
        return x.Dis>y.Dis;
    }
    priority_queue<Node> PQ;
    template <typename T>
    inline int Read(T &x) {
        x=0;
        int f=1;
        char c=getchar();
        while(c!='-'&&c>'9'&&c<'0')
            c=getchar();
        for(; !isdigit(c); c=getchar())
            if(c=='-')
                f=-f;
        for(; isdigit(c); c=getchar())
            x=x*10+c-'0';
        x*=f;
        if(c=='
    ')
            return 1;
        else
            return 0;
    }
    inline void Write(long long x) {
        if(x<0) {
            putchar('-');
            x=-x;
        }
        if(x>9) {
            Write((x-x%10)/10);
        }
        putchar(x%10+'0');
    }
    //这回是正常的快速输入输出
    inline ll Gcd(ll a, ll b) {
        if(b == 0)
            return a;
        else
            return Gcd(b, a%b);
    }
    inline void Add(int a,int b) {
        To[++Cnt]=a;
        Next[Cnt]=Head[b];
        Head[b]=Cnt;
        To[++Cnt]=b;
        Next[Cnt]=Head[a];
        Head[a]=Cnt;
    }
    void Dijkstra(int x) {
        Dist[x]=w[x];
        Now.Dis=w[x];
        Now.p=x;
        PQ.push(Now);
        while(!PQ.empty()) {
            Now=PQ.top();
            PQ.pop();
            if(!Pd[Now.p]) {
                Pd[Now.p]=1;
                for(int i=Head[Now.p]; i; i=Next[i]) {
                    if(!Pd[To[i]]&&(Dist[To[i]]>Now.Dis)) {
                        if(To[i]==v){
                        	Da=Now.Dis;
                            return;
                        }
                        Dist[To[i]]=Now.Dis;
                        N.p=To[i];
                        N.Dis=Dist[To[i]]+w[To[i]];
                        PQ.push(N);
                    }
                }
            }
        }
    }
    int main() {
        Read(T);
        Read(n);
        Cnt=0;
        for(int i=2;i<=n;i++){
        	for(int j=i+1;j<=n;j++){
        		if(Gcd(i,j)==1){
        			Add(i,j);
                }
            }
        }
        while(T--) {
            fill(Dist,Dist+n+5,INF);
            memset(Pd,0,sizeof(Pd));
            Read(u);
            Read(v);
            for(int i=1; i<=n; i++) {
                Read(w[i]);
            }
            Dijkstra(u);
            Write(Da);
            putchar('
    ');
            while(!PQ.empty()){
            	PQ.pop();
            }
        }
    
        return 0;
    }
    
  • 相关阅读:
    servlet 将输入内容通过拼接页面的方式显示出来
    localstorage和vue结合使用
    vue的通讯与传递props emit (简单的弹框组件)
    jquery插件之选项卡
    详解Cookie纪要
    jsonp
    滚动条样式
    axios基本使用
    IOS安卓常见问题
    简单购物车
  • 原文地址:https://www.cnblogs.com/zhudafu/p/11483048.html
Copyright © 2020-2023  润新知