@xiaoziyao
2021-04-20T10:31:53.000000Z
字数 1528
阅读 1410
解题报告
P7516 [省选联考 2021 A/B 卷] 图函数解题报告:
给定一张个点条边的有向图,定义函数的值为下列操作的返回值:
设,然后按到的顺序枚举点,如果中与能双向到达,那么,并删去结点,最后返回。
定义,并定义为图删除边后的图,求出。
。
定义为经过的点能否到达,同样设为经过的点能否到达,那么对于图,求的其实就是。
发现如果图改变需要重新处理值,这肯定会超时,因此将中是否能到达改为删除最多的边使得能让其到达的边数(无法到达则为)。
那么如果我们处理出,我们可以求出答案的差分数组,然后前缀和就好了。
考虑类似Floyd的做法,从大到小枚举中转点(这样做可以让循环到时经过的点全部大于等于,便于统计答案),然后对路径进行合并。
具体地,我们对于且可以经过的结点到达(其实就是判断当时的是否非零)的点,枚举终点,那么会存在一条的路径,且不难发现这条路径删除的边数为,用这个权值更新就好了。
同理对于且可以经过的结点到达的点,枚举终点(这里的要小于,否则答案会算重。但其实算重也没关系,因为这一部分的答案已经在之前更新过了),按上面同样的方式更新答案就好了。
时间复杂度:。
#include<stdio.h>const int maxn=1005,maxm=200005;int n,m;int g[maxn][maxn],sum[maxm],ans[maxm];inline int min(int a,int b){return a<b? a:b;}inline int max(int a,int b){return a>b? a:b;}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)g[i][i]=m+1;for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);g[x][y]=i;}for(int k=n;k>=1;k--){for(int i=k;i<=n;i++)sum[min(g[i][k],g[k][i])]++;for(int i=1;i<=k;i++)if(g[i][k])for(int j=1;j<=n;j++)g[i][j]=max(g[i][j],min(g[i][k],g[k][j]));for(int i=k+1;i<=n;i++)if(g[i][k])for(int j=1;j<k;j++)g[i][j]=max(g[i][j],min(g[i][k],g[k][j]));}for(int i=m;i>=1;i--)sum[i]+=sum[i+1];for(int i=1;i<=m+1;i++)printf("%d%c",sum[i],i==m+1? '\n':' ');return 0;}
省选联考A卷全部题解可见:2021省选联考A卷解题报告
