Codeforces 420 Coder-Strike 2014 - Finals (online edition, Div. 1) C. Bug in Code

問題

n人の人がいて、この中の二人が犯人。
n人のそれぞれに犯人は誰だと思うと聞いた結果、i番目の人はa[i]とb[i]と答えた。


犯人の候補(x, y)は、x = a[i]またはy = a[i]またはx = b[i]またはy = b[i]を満たすとき、
i番目の人の同意を得られる。
最低p人の同意を得られる犯人の候補はいくつあるか求めよ。

制約条件

n≦3*10^5
0≦p≦n
1≦a[i], b[i]≦n
a[i]≠b[i]かつa[i]≠i, b[i]≠i

方針

人を頂点、a[i], b[i]を辺とするグラフを考える。
最低p人の同意を得られる犯人の候補とは、ふたつの頂点(v1, v2)の組で、
p本の辺をカバーしているもの。


v1を固定して考える。
v1によりカバーされた辺はv2によって2度カバーされたと数えてはいけないので、
v1から出ている辺を削除しておく。
この後で、次数がp - d(v1)以上の頂点の数を数えればよい。


頂点を数えるのをまともにやるとTLEなので、
次数xの頂点がいくつあるかをbinary indexed treeに入れておけば、
BITに1回足したり引いたりするだけで、頂点の個数の管理ができる。


で、毎回これをやっても、
各辺につき2回ずつ削除と戻す操作が行われるだけなので、
時間計算量O(nlogn)とかになって間に合う。

ソースコード

const int MX = 300010;
int n, p;
int deg[MX], bit[MX];
vi e[MX];

ll sum(int i){
	ll res = 0;
	for(i++; i; i -= i & -i) res += bit[i];
	return res;
}
void add(int i, int x){
	for(i++; i < MX; i += i & -i) bit[i] += x;
}

int main(){
	scanf("%d%d", &n, &p);
	rep(i, n){
		int a, b;
		scanf("%d%d", &a, &b);
		e[a].pb(b);
		e[b].pb(a);
	}
	int cnt = 0;
	ll ans = 0;
	
	for(int i = 1; i <= n; i++){
		
		deg[i] = e[i].size();
		add(deg[i], 1);
	}
	
	for(int i = 1; i <= n; i++){
		
		rep(j, e[i].size()){
			add(deg[e[i][j]], -1);
			add(--deg[e[i][j]], 1);
		}
		if(deg[i] >= p) ans += n - 1;
		else{
			ans += n - 1 - sum(p - deg[i] - 1) + (deg[i] <= p - deg[i] - 1);
		}
		
		rep(j, e[i].size()){
			add(deg[e[i][j]], -1);
			add(++deg[e[i][j]], 1);
		}
	}
	cout << ans / 2 << endl;
	return 0;
}