[省选联考 2021 AB 卷] 图函数
Sol
题意转化后变成:
\(f(u,G)\) 表示,有多少个 \(v\),满足存在一条路径 \(u\to v\) 上没有比 \(v\) 的编号小的点。然后求倒着加边的过程中的 \(\sum\limits_{i=1}^nf(i,G)\)。
我们首先考虑求 \(f(u,G)\),这个怎么做?我们考虑当 \(u\to v\) 的路径上,\(u<v\) 的时候,显然这条路径不合法,所以对于 \(u\) 而言,对它的答案有贡献的只有小于等于它的点。所以我们确定有 \(v\le u\)。
这样我们就转化为:求对于编号在 \([v,n]\) 的点构成的图中,\(u\to v\) 是否可达。这个东西可以用 Floyd 快速搞出所有的点的情况。到这里我们已经能够求 \(\sum\limits_{i=1}^nf(i,G)\) 了。具体的,你设 \(dp[i][j]\) 表示仅通过 \([\min(i,j),n]\) 的点,\(i\) 到 \(j\) 是否可达。
接下来考虑倒着加边的问题。你发现如果仅仅用上面的方法很难实现加边的操作。这时候一个人类智慧,在遇到这种题的时候,如果直接求不好求,可以试着求两次答案的差值,然后再求前缀和出答案。
比如说这题,实际上这题单次询问要我们求的是,有多少对点 \(i,j\) 在不经过小于等于 \(\min(i,j)\) 的点中二者互相可达。你求 \(dp[i][j]\) 表示仅经过 \([\min(i,j),n]\) 的点,在倒着加边的过程中,\(i\) 到 \(j\) 联通的最短时刻。这样我们就可以知道当前点对的贡献是在什么时候作出的,然后我们求一个后缀和就可以啦。
这个转移的话,我们考虑 Floyd。你枚举中间点 \(k\),然后用 \(\min(dp[i][k],dp[k][j])\) 来更新当前的 \(dp[i][j]\),注意这里的最小值实际上是最晚的时间,所以在更新 \(dp[i][j]\) 的时候要用 \(\max\)。然后还有一个细节,就是你在枚举 \(j\) 的时候,我们必须保证路径上的点 \(\ge \min(i,j)\)。所以考虑 \(dp[i][k]\) 和 \(dp[k][j]\),如果 \(i>k\),那么 \(dp[i][k]\) 路径上的点只是保证大于 \(k\),所以 \(j<k\) 是必须的,否则,路径上的点一定是大于 \(i\) 的,那 \(j\) 取任何值都是可行的了。
Code
// Problem: P7516 [省选联考 2021 A/B 卷] 图函数
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7516
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// Time: 2022-04-16 15:25:33
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
using namespace std;
const int MAXN=1e3+10;
const int MAXM=2e5+10;
int dp[MAXN][MAXN],ans[MAXM];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m;cin>>n>>m;
rep(i,1,m){
int x,y;cin>>x>>y;
dp[x][y]=i;
}
per(k,n,1) rep(i,1,n) if(dp[i][k]) rep(j,1,i>k?k-1:n)
dp[i][j]=max(dp[i][j],min(dp[i][k],dp[k][j]));
rep(i,1,n) rep(j,i+1,n) ans[min(dp[i][j],dp[j][i])]++;
ans[m+1]=n;per(i,m,1) ans[i]+=ans[i+1];
rep(i,1,m+1) cout<<ans[i]<<' ';
return 0;
}