@xiaoziyao
2021-04-20T18:31:53.000000Z
字数 1528
阅读 1002
解题报告
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卷解题报告