[关闭]
@xiaoziyao 2021-04-20T10:31:53.000000Z 字数 1528 阅读 821

P7516 [省选联考 2021 A/B 卷] 图函数解题报告

解题报告


P7516 [省选联考 2021 A/B 卷] 图函数解题报告:

更好的阅读体验

题意

给定一张个点条边的有向图,定义函数的值为下列操作的返回值:

,然后按的顺序枚举点,如果能双向到达,那么,并删去结点,最后返回

定义,并定义为图删除边后的图,求出

分析

定义经过的点能否到达,同样设经过的点能否到达,那么对于图,求的其实就是

发现如果图改变需要重新处理值,这肯定会超时,因此将中是否能到达改为删除最多的边使得能让其到达的边数(无法到达则为)。

那么如果我们处理出,我们可以求出答案的差分数组,然后前缀和就好了。

考虑类似Floyd的做法,从大到小枚举中转点(这样做可以让循环到时经过的点全部大于等于,便于统计答案),然后对路径进行合并。

具体地,我们对于且可以经过的结点到达(其实就是判断当时的是否非零)的点,枚举终点,那么会存在一条的路径,且不难发现这条路径删除的边数为,用这个权值更新就好了。

同理对于且可以经过的结点到达的点,枚举终点(这里的要小于,否则答案会算重。但其实算重也没关系,因为这一部分的答案已经在之前更新过了),按上面同样的方式更新答案就好了。

时间复杂度:

代码

  1. #include<stdio.h>
  2. const int maxn=1005,maxm=200005;
  3. int n,m;
  4. int g[maxn][maxn],sum[maxm],ans[maxm];
  5. inline int min(int a,int b){
  6. return a<b? a:b;
  7. }
  8. inline int max(int a,int b){
  9. return a>b? a:b;
  10. }
  11. int main(){
  12. scanf("%d%d",&n,&m);
  13. for(int i=1;i<=n;i++)
  14. g[i][i]=m+1;
  15. for(int i=1;i<=m;i++){
  16. int x,y;
  17. scanf("%d%d",&x,&y);
  18. g[x][y]=i;
  19. }
  20. for(int k=n;k>=1;k--){
  21. for(int i=k;i<=n;i++)
  22. sum[min(g[i][k],g[k][i])]++;
  23. for(int i=1;i<=k;i++)
  24. if(g[i][k])
  25. for(int j=1;j<=n;j++)
  26. g[i][j]=max(g[i][j],min(g[i][k],g[k][j]));
  27. for(int i=k+1;i<=n;i++)
  28. if(g[i][k])
  29. for(int j=1;j<k;j++)
  30. g[i][j]=max(g[i][j],min(g[i][k],g[k][j]));
  31. }
  32. for(int i=m;i>=1;i--)
  33. sum[i]+=sum[i+1];
  34. for(int i=1;i<=m+1;i++)
  35. printf("%d%c",sum[i],i==m+1? '\n':' ');
  36. return 0;
  37. }

省选联考A卷全部题解可见:2021省选联考A卷解题报告

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注