• CF1062F Upgrading Cities


    Link
    一个思路是计算出每个点可以到达的点集和可以到达该点的点集的并的大小(S_u),但是很显然这样做的复杂度不会低于(O(frac{n^2}{omega}))
    但是注意到我们并不需要精确地计算出所有点的(S),对于一定不可能在答案中的点(u),我们可以不计算该点的(S)
    考虑利用拓扑排序计算该点可以到达的点数,计算完之后把所有边反向再计算可以到达该点的点数,途中维护尚未入队的点数(res)
    不难发现任意时刻队列中的点之间都没有连边。
    如果当前队列中有超过两个点,那么这些点一定都是不可能被计入答案的,因此我们可以直接无视。
    如果当前队列中仅有一个点(u),那么这个点可以到达剩下尚未入队的点,因此我们让(S_u)加上(res)
    如果当前队列中有两个点(u,v),不妨考虑只处理(u),如果(v)存在某个出点(w)入度为(1),那么说明(u)点到达不了(w),再加上(u)无法到达(v)(u)就可以被无视了。否则(v)的所有出点入度都大于(1),那么(u)一定可以到达(v)的所有出点,因此我们让(S_u)加上(res)
    这样做的话时间复杂度是(O(n+m))的。

    #include<queue>
    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    const int N=300007;
    int n,m,u[N],v[N],deg[N],cnt[N];std::vector<int>e[N];std::queue<int>q;
    int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
    int check(int u){for(int v:e[u])if(deg[v]==1)return 0;return 1;}
    void work()
    {
        int res=n;
        for(int i=1;i<=n;++i) e[i].clear();
        for(int i=1;i<=m;++i) e[u[i]].push_back(v[i]),++deg[v[i]];
        for(int i=1;i<=n;++i) if(!deg[i]) q.push(i),--res;
        for(int u;!q.empty();)
        {
    	u=q.front(),q.pop();
    	if(q.empty()) cnt[u]+=res; else if(q.size()==1) cnt[u]+=check(q.front())*res;
    	for(int v:e[u]) if(!--deg[v]) q.push(v),--res;
        }
    }
    int main()
    {
        n=read(),m=read();int ans=0;
        for(int i=1;i<=m;++i) u[i]=read(),v[i]=read();
        work();
        for(int i=1;i<=m;++i) std::swap(u[i],v[i]);
        work();
        for(int i=1;i<=n;++i) ans+=cnt[i]>=n-2;
        printf("%d",ans);
    }
    
  • 相关阅读:
    CodeForces 705A(训练水题)
    --hdu 2602 Bone Collector (01背包)
    --Dirring love 音乐(01背包问题)
    简单的网络拓扑
    --hdu 1231 最大连续子序列(动态规划)
    hdu 1003 Max Sum(动态规划)
    hdu 1284 钱币兑换问题(动态规划)
    hdu 1176 免费馅饼(动态规划)
    hdu 2084 数塔(动态规划)
    --hdu 2124 Repair the Wall(贪心)
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12669395.html
Copyright © 2020-2023  润新知