P7516 [省选联考 2021 A/B 卷] 图函数

本文解析了一种纯人类智慧问题的解决方案,利用Floyd-Warshall算法更新节点间距离,同时结合BFS确保图联通性检查。核心在于避免大范围点转移带来的问题,通过限制转移范围和迭代优化,实现在大规模图中高效计算贡献。
摘要由CSDN通过智能技术生成

解析

纯纯的人类智慧题。

关键性质: v v v 可以在计算 f ( u , G ) f(u,G) f(u,G) 时产生贡献,当且仅当 G G G u , v u,v u,v 之间可以通过 [ v , n ] [v,n] [v,n] 的点互相到达。
充分性较为显然,编号更大的点不会比 v v v 先删去,所以必然在 v v v 时刻依然存在。
必要性证明:假设 v v v 时刻 u → v u\to v uv 的路径上存在一个 x ∈ [ 1 , v ) x\in [1,v) x[1,v),在 x x x 时刻图的联通性不弱于 v v v 时刻,因而必然也有 u → x u\to x ux 可达,且由于 x → v , v → u x\to v,v\to u xv,vu 皆可达,也就有 x → u x\to u xu 可达, x x x 应当被删去,矛盾。

接下来的任务就是如何维护这个东西。

floyd

对于点编号属于 [ v , n ] [v,n] [v,n] 这样的限制,不难想到 floyd。但是直接跑无法通过 n = 1000 n=1000 n=1000
然后拿大样例试了试,发现写成:

for(int k=n;k>=1;k--){
		for(int i=1;i<=k;i++){
			for(int j=i;j<=n;j++){
			  k -> update(i,j)
			}
		}
	}

答案依然是对的,并且能跑过1000了。
然后就完事了。

我们当然要想一想为什么这样是对的。
这么转移必然出问题的就是两边的点编号均大于 k k k 的情况。
如图中的 p , q p,q p,q
(高度表示点编号的相对大小)
请添加图片描述

那么我们现在就要证明:

( x , y ) (x,y) (x,y) 路径上没有经过 [ 1 , min ⁡ ( x , y ) ) [1,\min(x,y)) [1,min(x,y)) 的点,就必然可以正确转移。

假设枚举到中转点 k k k 之前的路径都符合这个性质。
对于当前枚举的中转点 k k k,注意到我们需要求出的合法路径必然至少有一个端点是小于 k k k 的。
那么就必然能在 k k k 的某一侧找到第一个编号小于 k k k 的点 x x x k → x k\to x kx 这段路径使用 ( k , n ] (k,n] (k,n] 的点中转,不会出现现在 p , q p,q p,q 这样的窘境,所以 f ( k , x ) f(k,x) f(k,x) 当前是对的。
那么 x x x 就可以通过 k k k 连接起另一侧的 p p p ,得到正确的 f ( p , x ) f(p,x) f(p,x)
如果 ( u , p ) (u,p) (u,p) 之间有 k ′ k' k 这样的点导致 f ( u , p ) f(u,p) f(u,p) 不对怎么办?把 ( u , p ) (u,p) (u,p) 最小的点 k ′ k' k 当成 p p p 的角色来考虑,刚才的证明就还是对的。

bfs

本题看题解,还有通过 bfs 求对每个 v v v 考虑在每张图中能对多少 u u u 产生贡献的方法来做的,每次 bfs 用到一条边就直接删去,从而保证复杂度为 O ( n m ) O(nm) O(nm),也挺妙的。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=1050;
const int M=2e5+100;
const int inf=1e9;
const int mod=19921228;

inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}

int n,m;
int f[N][N];
ll sum[M];

signed main() {
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	//memset(f,0x3f,sizeof(f));
	n=read();m=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		f[x][y]=i;
	}
	for(int i=1;i<=n;i++) f[i][i]=m+1;
	for(int k=n;k>=1;k--){
		for(int i=k;i<=n;i++) sum[min(f[i][k],f[k][i])]++;
		for(int i=1;i<=k;i++){
			//if(!f[i][k]&&!f[k][i]) continue;
			for(int j=i;j<=n;j++){
				f[i][j]=max(f[i][j],min(f[i][k],f[k][j]));
				f[j][i]=max(f[j][i],min(f[j][k],f[k][i]));
			}
		}
	}
	for(int i=m;i>=1;i--) sum[i]+=sum[i+1];
	for(int i=1;i<=m+1;i++) printf("%lld ",sum[i]);
	return 0;
}
/*
*/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值