这题很容易就可以看出来,只要算出传递闭包,再减去出现过的,就可以得出结果了(虽然正解是dfs)。这里我们采用Warshall算法来计算,但是Warshall是
O
(
n
3
)
O(n^3)
O(n3)的,所以我们通过二进制来优化。但是就算
l
o
n
g
l
o
n
g
long long
longlong也存不下那么大的数,这个时候,把它分成50位一组,这个时候的时间复杂度是
2000
∗
2000
∗
40
2000*2000*40
2000∗2000∗40,就可以美美通过了。
下面是代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
#define int long long
int n, m, k;
int a[N][N];
int b[N][N];
int c[N][100];
void solve(){
cin >> n >> m;
for(int i = 1; i <= m; i++){
int x, y;
cin >> x >> y;
b[x][y] = a[x][y] = 1;
}
int cnt = 0;
for(int i = 1; i <= n; i++){
int temp = 0;
cnt = 0;
for(int j = 1; j <= n; j++){
//分成50个一组
if(temp == 0){
cnt ++;
}
c[i][cnt] |= (a[i][j] << temp);
temp ++;
if(temp == 50){
temp = 0;
}
}
}
int ans = 0;
int temp = 0;
for(int i = 1; i <= n; i++){
int temp1 = (i - 1) / 50 + 1;
for(int j = 1; j <= n; j++){
if((c[j][temp1] >> temp) & 1){
for(int k = 1; k <= cnt; k++){
c[j][k] |= c[i][k];
}
}
}
temp ++;
if(temp == 50){
temp = 0;
}
}
for(int i = 1; i <= n; i++){
int temp1 = 0, cnt1 = 0;
for(int j = 1; j <= n; j++){
if(temp1 == 0){
cnt1 ++;
}
//题目说了x != y,所以i==j的时候就不计算了
if(i != j && ((c[i][cnt1] >> temp1) & 1) != b[i][j]){
ans ++;
}
temp1 ++;
if(temp1 == 50){
temp1 = 0;
}
}
}
cout << ans << "\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while(t--){
solve();
}
}
下面补一下dfs的代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
#define int long long
int n, m, k;
vector<int> v[N];
int vis[N];
int ans;
void dfs(int x){
for(int i : v[x]){
if(vis[i]){
continue;
}
vis[i] = 1;
ans ++;
dfs(i);
}
}
void solve(){
cin >> n >> m;
for(int i = 1; i <= m; i++){
int x, y;
cin >> x >> y;
v[x].push_back(y);
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
vis[j] = 0;
}
vis[i] = 1;
dfs(i);
}
cout << ans - m << "\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while(t--){
solve();
}
}