zoukankan      html  css  js  c++  java
  • [某模拟赛]一道好题

    给定一张N个点N条边的有向图,每个点出度为1。

    求最少增加多少条有向边使得:从1到每个点的最短路径不大于K。

    显然先要从出度为0的点开始将外面树上的所有点搞掉。

    问题就转化为剩下一个环,并且环上有一些点已经染好色,可以将其中连续K个点染色,问将其全部染好的最小次数。

    可以从某点开始染,预处理每个点隔K个之后下一个未染色的点。这样做一次是O(N/k)。

    可以发现其实只要将任意连续K个未染色的点都做一次就保证能出最优解。总复杂度为O(K)*O(N/K)=O(N)。

    算法其实不难,但尼玛细节搞死人啊!!!%>_<%

    调了半天,最后还是靠std对拍才勉强搞到AC的。

    树的预处理比较恶心,我开始算法也有bug。

    环的细节还是不少的,写了各种版本,最后还是错了。

    自己YY的东西还是想清楚,保证无误再写才好啊。

    /*
    
        Problem:
    
        Algorithm:
    
        Note:
    
    */
    
    #include<cstdio>
    
    #include<cstring>
    
    #include<algorithm>
    
    #include<set>
    
    #include<vector>
    
    #include<map>
    
    #include<string>
    
    #include<iomanip>
    
    #include<iostream>
    
    #include<cmath>
    
    #include<queue>
    
    using namespace std;
    
    
    
    #define rep(i,x,y) for(i=x;i<=y;i++)
    
    #define _rep(i,x,y) for(i=x;i>=y;i--)
    
    #define CL(S,x) memset(S,x,sizeof(S))
    
    #define CP(S1,S2) memcpy(S1,S2,sizeof(S2))
    
    #define ALL(x,S) for(x=S.begin();x!=S.end();x++)
    
    #define sqr(x) ((x)*(x))
    
    #define mp make_pair
    
    #define fi first
    
    #define se second
    
    #define upmin(x,y) x=min(x,y)
    
    #define upmax(x,y) x=max(x,y)
    
    
    
    typedef long long ll;
    
    typedef long double ld;
    
    void read(int&x){bool fu=0;char c;for(c=getchar();c<=32;c=getchar());if(c=='-')fu=1,c=getchar();for(x=0;c>32;c=getchar())x=x*10+c-'0';if(fu)x=-x;};
    
    char getc(){char c;for(c=getchar();c<=32;c=getchar());return c;}
    
    
    
    const int N=500010;
    
    const int inf=1000000000;
    
    int n,K,i,j,p,x,y,z,w,g,huan,have,totl,start,it;
    
    int cs[N],dis[N],d[N],next[N],path[N],ans,q[N],xia[N],go[N],cnt,l[N],qian[N];
    
    bool v[N],cir[N],need[N];int ti[N];
    
    
    
    void markcir()
    
    {
    
        rep(p,1,n)if(!v[p])
    
        {
    
            i=p;while(!v[i]){ti[i]=p;v[i]=1;i=next[i];}
    
            if(ti[i]!=p)continue;
    
            huan++;cs[huan]=i;
    
            start=i;do{cir[i]=1;i=next[i];}while(i!=start);
    
        }
    
    }
    
    
    
    void bfs()
    
    {
    
        CL(dis,127);
    
        j=0;rep(i,2,n)if(d[i]==0)q[++j]=i,dis[i]=1,ans++;
    
        if(d[1]==0)q[++j]=1,dis[1]=0;
    
        if(cir[1])q[++j]=1,dis[1]=0;
    
        
    
        for(i=1;i<=j;i++)
    
        {
    
            p=next[q[i]];w=dis[q[i]]+1;   
    
            if(cir[p])
    
            {
    
                if(w<=K&&w<dis[p])
    
                {
    
                    dis[p]=w;q[++j]=p;
    
                }
    
            }
    
            else
    
            {
    
                upmin(dis[p],w);
    
                if(--d[p]==0)
    
                {
    
                    if(p==1)dis[p]=0;
    
                    else if(dis[p]>K){dis[p]=1,ans++;}
    
                    q[++j]=p;
    
                }
    
            }
    
        }    
    
        rep(i,1,n)if(cir[i]&&dis[i]==dis[0])need[i]=1,have++;
    
    }
    
    
    
    int u;
    
    bool pdin(int i,int l,int r)
    
    {
    
        if(l<=r)return i>=l&&i<=r;
    
        return i<=r||i>=l;
    
    }
    
    int solvecir(int start)
    
    {
    
        i=start;g=have=0;
    
        do{path[++g]=i;if(need[i])have++;i=next[i];}while(i!=start);
    
    
    
        if(have<=1)return have;
    
        if(g<=K)return 1;
    
        
    
        rep(i,1,g)if(need[path[i]])break;start=i;
    
        xia[start]=start;p=start;
    
        for(i=(start+g-2)%g+1;i!=start;i=(i+g-2)%g+1){if(need[path[i]])p=i;xia[i]=p;}
    
        
    
        int best=inf;
    
        i=start;cnt=0;
    
        while(1)
    
        {
    
            j=i;
    
            int res=0; 
    
            while(1)
    
            {
    
                res++;
    
                u=xia[(j+K-1)%g+1];
    
                if(pdin(i,j+1,u))break;
    
                j=u;
    
            }
    
            upmin(best,res);
    
            i=xia[i%g+1];++cnt;if(cnt>K)break;
    
        }
    
        return best;
    
    }
    
    int main()
    
    {
    
        freopen("B.in","r",stdin);
    
        freopen("B.out","w",stdout);
    
        read(n);read(K);rep(i,1,n)read(x),read(y),next[x]=y,d[y]++;
    
    
    
        markcir();
    
        bfs();
    
        
    
        rep(it,1,huan)
    
        ans+=solvecir(cs[it]);
    
        
    
        printf("%d
    ",ans);
    
        
    
        scanf("
    ");
    
        return 0;
    
    }
    
  • 相关阅读:
    [APIO2012]派遣
    Luogu_2774 方格取数问题
    Luogu4149 [IOI2011]Race
    Luogu_4886 快递员
    Loj_6282. 数列分块入门 6
    LOJ 6281 数列分块入门 5
    三维偏序 cdq
    【逆序对】 模板
    【luogu P1637 三元上升子序列】 题解
    【luogu P3609 [USACO17JAN]Hoof, Paper, Scissor蹄子剪刀布】 题解
  • 原文地址:https://www.cnblogs.com/oldmanren/p/3378210.html
Copyright © 2011-2022 走看看