题目大意:
给你一个有向无环图,一开始所有点都是白色的点,现在每次操作给一个点v,将他的颜色翻转(白色点变为黑色点,黑色点变为白色点),你需要在每次操作之后,回答这个图中有多少对(u,v)节点,u,v节点为白色,且u到v有一条全部为白色节点的路径。
思路:
如果每次询问求一次任意两点之间是否相通,复杂度为O(n^4),无法接受,不过有一种类似暴力的方法,用bitset维护图,这样可以加速修改,贴上代码:
bitset维护:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,q;
const int N=3e2+10;
vector<int>G[N];
int col[N],du[N],d[N],tx[N];
int bfs()
{
bitset<310>bit[310];
queue<int>que;
for(int i=1;i<=n;++i){
d[i]=du[i],tx[i]=0;
bit[i][i]=1;
if(d[i]==0)que.push(i);
}
while(que.size())
{
int u=que.front();que.pop();
for(int v:G[u])
{
if(col[u]==0&&col[v]==0) {
bit[v]|=bit[u];
}
d[v]--;
if(d[v]==0) que.push(v);
}
}
int ans=0;
for(int i=1;i<=n;++i)
{
ans+=bit[i].count()-1;
}
return ans;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&q))
{
for(int i=1;i<=n;++i) col[i]=0,G[i].clear(),du[i]=0;
for(int i=1;i<=m;++i)
{
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v);
du[v]++;
}
while(q--)
{
int u;
scanf("%d",&u);
col[u]=1-col[u];
printf("%d\n",bfs());
}
}
}
但是这里用类似floyd的做法更加好一点,f(i,j)表示i到j的路径总数为多少,g(i,j)为原图的邻接矩阵,那么每次进来一个点v,我们可以枚举路径的起点i和终点j,分两种情况:
1.如果是白点变黑点,那么我们只需要减去通过这个点的路径总数就可以,并且更新一下g数组;
2.如果是黑点变白点,那么我们更新以v为起点,i为终点的新增路径数,再更新以v为终点,i为起点的新增路径数,最后再枚举起点i和终点j,更新答案就好了。这样的复杂度为O(n^3)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 307;
int f[N][N];//点u到点v的路径条数
int c[N][N];//原图,判断u到v是否有边
bool vis[N];
char s[N];
int main(){
//freopen("in.txt", "r", stdin);
int n, m, q;
while (scanf("%d%d%d", &n, &m, &q) != EOF){
memset(f, 0, sizeof(f)); memset(c, 0, sizeof(c));
for (int i = 1; i <= m; i++){
int u, v;
scanf("%d%d", &u, &v);
f[u][v] = c[u][v] = 1;
}
//求路径条数
for (int k = 1; k <= n; k++){
for (int i = 1; i < k; i++){
for (int j = k + 1; j <= n; j++){
f[i][j] = f[i][j] + f[i][k] * f[k][j];
}
}
}
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= q; i++){
int u; //u = rand() % n + 1;
scanf("%d", &u);
if (!vis[u]){//由白变黑,只需要判断减少了多少条边
vis[u] = true;
for (int j = 1; j < u; j++){
for (int k = u + 1; k <= n; k++){
f[j][k] = f[j][k] - f[j][u] * f[u][k];
}
}
for (int j = 1; j <= n; j++) f[j][u] = f[u][j] = 0;
} else {
//由黑变白,判断增加了多少条路径
vis[u] = false;
//更新以v为起点,i为终点的新增路径数
for (int j = u + 1; j <= n; j++) if (!vis[j]){
for (int k = u + 1; k < j; k++) if (c[u][k]){
f[u][j] += f[k][j];
}
f[u][j] += c[u][j];
}
//更新以v为终点,i为起点的新增路径数
for (int j = 1; j < u; j++) if (!vis[j]){
for (int k = j + 1; k < u; k++) if (c[k][u]){
f[j][u] += f[j][k];
}
f[j][u] += c[j][u];
}
//枚举起点i和终点j
for (int j = 1; j < u; j++){
for (int k = u + 1; k <= n; k++){
f[j][k] = f[j][k] + f[j][u] * f[u][k];
}
}
}
int ans = 0;
for (int j = 1; j <= n; j++){
for (int k = j + 1; k <= n; k++){
if (f[j][k]) ans++;
}
}
printf("%d\n", ans);
}
}
return 0;
}