zoukankan      html  css  js  c++  java
  • [省选联考 2021 A/B 卷] 图函数

    一、题目

    点此看题

    开始接受(...)痛苦不堪的回忆。

    二、解法

    你看它不用算具体的东西,只用算一个总和,这不用贡献法用什么?

    考虑 (v) 的贡献,也就是保留 ([v,n]) 的点和有关边时,和它能互通 (u) 点的个数。前 ([1,v)) 不用考虑是因为如果和 (u) 能互通就会删除,如果不互通那么和 (u) 不在一个强连通块内,对 (u,v) 是否能互通没有任何影响。

    枚举点 (v),然后跑 ( t tarjan),单次可以做到 (O(nm)) 的复杂度。

    先固定点 (v),考虑删除一个边的前缀的影响,把删边变成加边是老套路了,我们逆序来做。( t tarjan) 不好维护但考虑到有一个点是定点,所以建出正反图,然后动态 ( t bfs),如果加入的这条边两个端点都访问过就没用,如果终点没有访问过就以他开始 ( t bfs),每次把 ( t bfs) 到的边删掉,如果两个端点都没访问过就加入图中。每条边只会被删除一次,所以时间复杂度 (O(nm))


    还有一种方法是考虑点对 ((u,v)) 的贡献,也就是保留 ([v,n]) 的点和边时 (u,v) 能互通。因为本题边越多 (u,v) 更容易互通,所以我们给每个边一个时间(第 (i) 条边时间为 (i)),可以考虑求出 (u,v) 互通的最小瓶颈边最大的路径,那么可知这会贡献给答案序列的一个前缀。

    有向图的瓶颈边问题可以考虑 ( t floyd),设 (f[i][j]) 表示 (i)(j) 的最小瓶颈边的最大值,那么考虑枚举中转点 (k),有一个限制是 (kgeqmin(i,j)),因为图只能保留这些点,在内层循环的时候注意一下即可。

    还有一个细节是 (k) 要倒序枚举,首先考虑 ( t floyd) 的原理:对于每一条可能的路径,我们通过枚举中转点能把这些点一个一个拼起来,这相当于枚举了所有情况。但是这道题的路径可能是 small->big->middle->big->small,如果先枚举了small就会导致路径拼不起来,所以要先枚举big

    时间复杂度 (O(n^3)),真 ( t tm) 怎么做都可以,但是考试时就是不会。

    三、总结

    当不用求出具体值,只用求总和时,考虑贡献法。

    尝试多种翻译方式,比如这道题我一开始翻译的是 ((u,v)) 强联通,但是因为 ( t tarjan) 不好动态做所以卡了。但是如果翻译成 ((u,v)) 互通就利于后面想到动态 ( t bfs) 的方法。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 1005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,f[M][M],ans[200005];
    signed main()
    {
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int u=read(),v=read();
            f[u][v]=i;
        }
        for(int k=n;k>=1;k--)
        {
            for(int i=1;i<=n;i++)
            {
                if(!f[i][k]) continue;
                int t=f[i][k],up=(i>k)?(k-1):n;
                for(int j=1;j<=up;j++)
                    f[i][j]=max(f[i][j],min(t,f[k][j]));
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                ans[min(f[i][j],f[j][i])]++;
        ans[m+1]=n;
        for(int i=m;i>=1;i--) ans[i]+=ans[i+1];
        for(int i=1;i<=m+1;i++) printf("%d ",ans[i]);
    }
    
    
  • 相关阅读:
    Git的使用
    工具使用--Tomcat
    数据库优化-索引
    sql语句小练习
    Oracle sql 优化
    用词云图分析一带一路峰会哪3个词说的最多
    为什么你用不好Numpy的random函数?
    python3.6下安装结巴分词需要注意的地方
    UFO长啥样?--Python数据分析来告诉你
    关于matplotlib,你要的饼图在这里
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15024721.html
Copyright © 2011-2022 走看看