zoukankan      html  css  js  c++  java
  • Luogu P5304 [GXOI/GZOI2019]旅行者

    有趣的题目,又好想又好码,可谓是省选题中的一股清流

    考虑如果我们枚举一个点作为起点,然后暴力求出到其它点的最短路,那么可以暴力解决问题

    但是我们稍微转化一下问题,同时把一些点的集合作为起点,跑出到其它剩下所有点的最短路,取出其中最小的一条,就相当于同时做了多次猜测

    具体实现也非常简单,直接建个超级起点(st)和终点(tar),如果这个关键点(x)位于起点集合那么连一条(st o x,val=0)的边,在终点集合就连一条(x o tar,val=0)的边

    最后(st o tar)的最短路即为答案,看起来十分简单

    但是我们细细一想发现这个方法好像很优秀,如果最后答案是(x o y),那么如果(x)被分到起点集合里而(y)被分到终点集合里那么这个算法就直接跑出了答案!

    根据上面的分析我们很容易得到一个随机化的做法,我们每次给所有关键点随机分配到两边然后跑最短路,这样正确率就是(frac{1}{4})

    由于跑一次的复杂度是(O(nlog n))(用DJ),因此5s的时限我们稳妥的跑(20)次左右,这样错误的概率就是:

    [(frac{1}{4})^{20}approx 0.0031712approxfrac{1}{315} ]

    足以通过此题,而且最大点在Luogu上仅用时2100+ms,因此跑更大的(30,40)次都不成问题(如果比赛的时候还是求稳为主,后面全T了就很尴尬)

    随机化CODE

    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<queue>
    #define RI register int
    #define CI const int&
    #define Tp template <typename T>
    using namespace std;
    const int N=1e5+5,M=5e6+5;
    const long long INF=1e18;
    struct edge
    {
        int to,nxt,v;
    }e[N+M]; int n,t,thead[N],head[N],a[N],cnt,tcnt,m,k,x,y,z,st,tar; long long dis[N];
    class FileInputOutput
    {
        private:
            static const int S=1<<21;
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
            char Fin[S],*A,*B;
        public:
            Tp inline void read(T& x)
            {
                x=0; char ch; while (!isdigit(ch=tc()));
                while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
            }
            #undef tc
    }F;
    class SSSP
    {
        private:
            struct data
            {
                int id; long long val;
                friend inline bool operator < (const data& A,const data& B)
                {
                    return A.val>B.val;
                }
            }; priority_queue <data> hp; bool vis[N];
        public:
            #define to e[i].to
            inline void Dijkstra(void)
            {
                RI i; for (i=st;i<=tar;++i) dis[i]=INF,vis[i]=0; dis[st]=0;
                hp.push((data){st,0}); while (!hp.empty())
                {
                    int now=hp.top().id; hp.pop(); if (vis[now]) continue;
                    vis[now]=1; for (i=head[now];i;i=e[i].nxt)
                    if (dis[to]>dis[now]+e[i].v) hp.push((data){to,dis[to]=dis[now]+e[i].v});
                }
            }
            #undef to
    }G;
    inline void taddedge(CI x,CI y,CI z)
    {
        e[++tcnt]=(edge){y,thead[x],z}; thead[x]=tcnt;
    }
    inline void addedge(CI x,CI y,CI z)
    {
        e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
    }
    inline long long solve(long long ret=INF)
    {
        RI i,j; for (F.read(n),F.read(m),F.read(k),i=1;i<=m;++i)
        F.read(x),F.read(y),F.read(z),taddedge(x,y,z);
        for (tar=n+1,i=1;i<=k;++i) F.read(a[i]);
        for (i=1;i<=20;++i)
        {
            for (cnt=tcnt,j=st;j<=tar;++j) head[j]=thead[j];
            for (j=1;j<=k;++j) if (rand()&1) addedge(st,a[j],0);
            else addedge(a[j],tar,0); if (G.Dijkstra(),dis[tar]<ret) ret=dis[tar];
        }
        return ret;
    }
    inline void clear(void)
    {
        for (RI i=st;i<=tar;++i) thead[i]=0; tcnt=0;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        for (srand(20030909),F.read(t);t;--t) printf("%lld
    ",solve()),clear(); return 0;
    }
    

    好吧接下来讲一下正确做法,大致建模和上面一样,就是分配点集的时候稍作修改,枚举(n)范围内的每一个二进制位,如果这个点编号这一位上为(1)就放在起点一边,反之

    正确性证明也很好理解,假设答案为(x o y),那么(x e y),因此(x,y)必然有至少一位在二进制下不同,故至少有一次被分在了两侧,结论成立

    这样就可以以(O(nlog^2 n))的复杂度通过此题了,注意答案为有向点对,因此要枚举两边

    二进制分组CODE

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #define RI register int
    #define CI const int&
    #define Tp template <typename T>
    using namespace std;
    const int N=1e5+5,M=5e6+5;
    const long long INF=1e18;
    struct edge
    {
        int to,nxt,v;
    }e[N+M]; int n,t,thead[N],head[N],a[N],cnt,tcnt,m,k,x,y,z,st,tar; long long dis[N];
    class FileInputOutput
    {
        private:
            static const int S=1<<21;
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
            char Fin[S],*A,*B;
        public:
            Tp inline void read(T& x)
            {
                x=0; char ch; while (!isdigit(ch=tc()));
                while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
            }
            #undef tc
    }F;
    class SSSP
    {
        private:
            struct data
            {
                int id; long long val;
                friend inline bool operator < (const data& A,const data& B)
                {
                    return A.val>B.val;
                }
            }; priority_queue <data> hp; bool vis[N];
        public:
            #define to e[i].to
            inline void Dijkstra(void)
            {
                RI i; for (i=st;i<=tar;++i) dis[i]=INF,vis[i]=0; dis[st]=0;
                hp.push((data){st,0}); while (!hp.empty())
                {
                    int now=hp.top().id; hp.pop(); if (vis[now]) continue;
                    vis[now]=1; for (i=head[now];i;i=e[i].nxt)
                    if (dis[to]>dis[now]+e[i].v) hp.push((data){to,dis[to]=dis[now]+e[i].v});
                }
            }
            #undef to
    }G;
    inline void taddedge(CI x,CI y,CI z)
    {
        e[++tcnt]=(edge){y,thead[x],z}; thead[x]=tcnt;
    }
    inline void addedge(CI x,CI y,CI z)
    {
        e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
    }
    inline long long solve(long long ret=INF)
    {
        RI i,j; for (F.read(n),F.read(m),F.read(k),i=1;i<=m;++i)
        F.read(x),F.read(y),F.read(z),taddedge(x,y,z);
        for (tar=n+1,i=1;i<=k;++i) F.read(a[i]);
        for (i=1;i<=(n<<1);i<<=1)
        {
            for (cnt=tcnt,j=st;j<=tar;++j) head[j]=thead[j];
            for (j=1;j<=k;++j) if (a[j]&i) addedge(st,a[j],0);
            else addedge(a[j],tar,0); if (G.Dijkstra(),dis[tar]<ret) ret=dis[tar];
        }
        for (i=1;i<=(n<<1);i<<=1)
        {
            for (cnt=tcnt,j=st;j<=tar;++j) head[j]=thead[j];
            for (j=1;j<=k;++j) if (a[j]&i) addedge(a[j],tar,0);
            else addedge(st,a[j],0); if (G.Dijkstra(),dis[tar]<ret) ret=dis[tar];
        }
        return ret;
    }
    inline void clear(void)
    {
        for (RI i=st;i<=tar;++i) thead[i]=0; tcnt=0;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        for (F.read(t);t;--t) printf("%lld
    ",solve()),clear(); return 0;
    }
    
  • 相关阅读:
    javascript设计模式——链式模式学习
    浏览器debug常用技巧
    前端到底要不要学后台
    坑爹的JS闭包,怎么去理解才是正确的
    如何更加简单的理解JS中的原型原型链概念
    前端那么多框架,我们到底学哪一个
    大前端之——数据交互
    随便写一点自己对前端的感受
    如何手动使用webpack搭建一个react项目
    浅谈 CSS 预处理器: 为什么要使用预处理器?
  • 原文地址:https://www.cnblogs.com/cjjsb/p/10964483.html
Copyright © 2011-2022 走看看