题目链接

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;
}