题目链接
https://codeforces.com/contest/1229/problem/C
题解
经过谷歌翻译之后的题面是:如果 \(a\) 吹牛 \(b\),那么谁吹牛 \(c\)
2400 的题我都不会……不过我真的觉得这题的复杂度分析神乎其神/kel
首先枚举 \(b\) 那个点,可得答案显然等于所有点的入度乘出度之和。考虑直接暴力维护,假设每次修改的时候我们遍历所有要修改的边,然后暴力更新。
神奇的势能分析告诉我们这个算法的时间复杂度是 \(O(n+m+q\sqrt m)\).
原因如下:考虑一个无向图,假设把所有点按度数从大到小排序,那么和一个点相连的排在它前面的点不超过 \(\sqrt{2m}\) 个。定义图的势能为从前往后连的边的个数,那么每次修改增加的势能显然不超过 \(\sqrt{2m}\),而每修改一条边的方向就会减少 \(1\) 的势能,然后就证完了。
简直是绝妙啊。
代码
#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
using namespace std;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
return x*f;
}
const int mxN = 1e5;
int n,en,m,q;
vector<int> ine[mxN+3];
int ind[mxN+3],oud[mxN+3];
int a[mxN+3];
int main()
{
n = read(),m = read();
for(int i=1; i<=m; i++) {int u = read(),v = read(); if(u<v) {swap(u,v);} ine[v].push_back(u); ind[v]++,oud[u]++;}
llong ans = 0ll; for(int i=1; i<=n; i++) ans += 1ll*ind[i]*oud[i];
printf("%lld\n",ans);
q = read();
while(q--)
{
int u = read(); ans -= 1ll*ind[u]*oud[u];
for(int i=0; i<ine[u].size(); i++)
{
int v = ine[u][i];
ine[v].push_back(u);
ans -= 1ll*ind[v]*oud[v]; ind[v]++,oud[v]--,ind[u]--,oud[u]++; ans += 1ll*ind[v]*oud[v];
}
ine[u].clear();
printf("%lld\n",ans);
}
return 0;
}