题面
样例
6 12 8
1 2
2 3
3 4
3 2
3 1
4 1
4 5
1 5
6 5
5 6
3 6
6 3
3
分析
可以说是一个套路/思维模式吧
不难想到 O ( n 3 / k ) \mathcal{O}(n^3/k) O(n3/k) ( k k k 为常数)的暴力,二分优化一下 O ( n 2 l o g 2 ( n ) ) \mathcal{O}(n^2log_2(n)) O(n2log2(n))。
考虑时间复杂度为什么会这么高,发现是二分时进行了太多冗余的操作。
考虑缩短二分上下界。不妨设现在要取的边的数量为 t t t,要找到一个 q q q,使 t ∈ [ 2 q , 2 q + 1 ) t\in[2^q,2^{q+1}) t∈[2q,2q+1)(可以直接找(log)或二分(log(log))),这是 O ( l o g 2 ( t ) × t ( × 2 ) ) \mathcal{O}(log_2(t)\times t(\times2)) O(log2(t)×t(×2)) 的,再在这里面二分,这是 O ( l o g 2 ( t ) × t ( × 2 ) ) \mathcal{O}(log_2(t)\times t(\times 2)) O(log2(t)×t(×2)) 的,总时间复杂度: O ( ∑ t × l o g 2 ( t ) ) ≤ O ( ∑ t × l o g 2 ( m ) ) = O ( m × l o g 2 ( m ) ) ( ( ∑ t ) = = m ) \mathcal{O}(\sum t\times log_2(t))\leq O(\sum t\times log_2(m))=O(m\times log_2(m))((\sum t) == m) O(∑t×log2(t))≤O(∑t×log2(m))=O(m×log2(m))((∑t)==m)
这优化就很妙。。。
Code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#define LL long long
using namespace std;
const int MAXN = 1e5 + 5, MAXM = 1e6 + 5;
int n, m, X[MAXM], Y[MAXM], tot, cnt, now = 1, col[MAXN], dfn[MAXN], low[MAXN];
int s[MAXN], stot, num, sl, sr, nk, clock, val;
LL ans, k;
int Head[MAXN], Ver[MAXM << 1], Next[MAXM << 1];
void add(int x, int y) { Ver[++ tot] = y; Next[tot] = Head[x]; Head[x] = tot; }
void read(int &x) {
x = 0; int f = 1; char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = 0;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
x = f ? x : -x;
}
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; }
void dfs(int x) {
dfn[x] = low[x] = ++ cnt; s[++ stot] = x; num ++;
for(int i = Head[x]; i; i = Next[i]) {
if(i < sl || i > sr) continue;
int Y = Ver[i];
if(!dfn[Y]) dfs(Y), low[x] = Min(low[x], low[Y]);
else if(!col[Y]) low[x] = Min(low[x], dfn[Y]);
}
if(dfn[x] == low[x]) {
int t; nk ++; clock = 0;
do {
t = s[stot --]; col[t] = nk; clock ++;
} while(t != x);
ans += (LL)clock * clock;
}
}
bool Check(int l, int r) {
num = 0; cnt = 0; ans = 0; sl = l; sr = r; stot = 0; nk = 0;
for(int i = l; i <= r; i ++) dfn[X[i]] = dfn[Y[i]] = low[X[i]] = low[Y[i]] = col[X[i]] = col[Y[i]] = 0;
for(int i = l; i <= r; i ++) {
if(!dfn[X[i]]) dfs(X[i]);
if(!dfn[Y[i]]) dfs(Y[i]);
}
ans += n - num;
// printf("|%d %d %lld|\n", l, r, ans);
return (ans <= k);
}
int main() {
read(n); read(m); scanf("%lld", &k);
for(int i = 1; i <= m; i ++) {
read(X[i]); read(Y[i]); add(X[i], Y[i]);
}
while(now <= m) {
int l = now, r = 0, len = 0, mid, res;
while(1) {
if(r < m && Check(l, Min(m, l + (1 << len)))) r = Min(m, l + (1 << len)), len ++;
else break;
}
if(r == m) { val ++; break; }
l = r; r = Min(m, now + (1 << len) - 1);
while(l <= r) {
mid = (l + r) >> 1;
if(Check(now, mid)) res = mid, l = mid + 1;
else r = mid - 1;
}
now = res + 1; val ++;// printf("|%d|", now);
}
printf("%d", val);
return 0;
}