【题解】[CCO2021] Bread First Search
题意:给定一个图,求最少需要加入多少条边使得图的 \(BFS\) 顺序可能为 \(1\sim N\)。
神仙题,首先得发现这是个线性 DP,并写出状态和方程,做到这里这题就完成了一半。
状态,我们定义 \(f_i\) 表示节点 \(1\sim i\) 的子图的答案。
转移 \(f_i + val(i+1, j) \to f_j\),条件为节点 \(1\sim i\) 和节点 \((j + 1)\sim n\) 之间没有边。
其中 \(val(i,j)\) 表示 \((i + 1)\sim j\) 中与 \(1\sim i\) 中节点有边的节点数。
不难发现 check 和 val 本质上是相同的,我们只用预处理 \(s_{i,j}\) 表示点 \(1\sim j\) 中与点 \(1\sim i\) 中节点有边的节点数即可。
其中 $s_i $ 是在 \(s_{i - 1}\) 的基础上改动几个,直接用可持久化线段树优化即可。
但是我们转移还是 \(N^2\) 的,考虑优化。根据套路,这题既没有斜率也无法单调队列,只有决策单调性。
打个表,或者感性理解一下,就能发现这个方程确实有决策单调性。直接队列优化即可。
时间复杂度 \(\mathcal{O}(N\log ^2 N)\) ,空间 \(\mathcal{O}(N\log N)\)。以下是 \(N^2\) 的算法。
#define N 5005
int n, m, f[N], s[N][N], g[N];
int main(){
//int T = read();while(T--)solve();
n = read(), m = read();
rp(i, m){
int x = read(), y = read();
if(x > y)swap(x, y);
s[x][y] = 1;
}
rp(i, n)rp(j, n)
s[i][j] = (s[i][j] | (s[i - 1][j] - s[i - 1][j - 1])) + s[i][j - 1];
memset(f, 0x3f, sizeof(f));
f[1] = 0;
rp(i, n - 1){
rep(j, i + 1, n){
int cur = f[i] - s[i][j] + s[i][i] + j - i;
if(!(s[i][n] - s[i][j]) && cur <= f[j])
f[j] = cur, g[j] = i;
}
}
rp(i, n - 1)assert(g[i] <= g[i + 1]);
printf("%d\n", f[n]);
return 0;
}
\(\mathcal{O}(N\log N)\)。
/*
Author : SharpnessV
Right Output ! & Accepted !
*/
#include<bits/stdc++.h>
//#define int long long
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define pre(i, a, b) for(int i = (a);i >= (b);i--)
#define rp(i, a) for(int i = 1; i <= (a); i++)
#define pr(i, a) for(int i = (a); i >= 1; i--)
#define go(i, x) for(auto i : x)
#define mp make_pair
#define pb push_back
#define pf push_front
#define fi first
#define se second
#define ze(p) memset(p, 0, sizeof(p))
#define mem(p, x) memset(p, x, sizeof(p))
#define YES puts("YES")
#define NO puts("NO")
#define Yes puts("Yes")
#define No puts("No")
#define si(x) (int)(x).size()
#define db cerr
#define pc putchar
#define gc getchar
#define el putchar('\n')
using namespace std;
const double eps = 1e-15, pi = 3.1415926535897932385;
typedef long long LL;
typedef pair<int,int> Pr;
const int dx[4] = {1,0,-1,0}, dy[4] = {0,1,0,-1}, inf = 0x7fffffff;
//Author : SharpnessV
//#define main Solution
//char buf[1<<22],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read(){
int x = 0;bool f = 1;char ch = gc();
while(!isdigit(ch))f = ('-' == ch ? 0 : 1), ch = gc();
while(isdigit(ch))x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc();
if(f)return x;return -x;
}
inline LL Read(){
LL x = 0;bool f = 1;char ch = gc();
while(!isdigit(ch))f = ('-' == ch ? 0 : 1), ch = gc();
while(isdigit(ch))x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc();
if(f)return x;return -x;
}
int gcd(int x,int y){return y ? gcd(y, x % y) : x;}
int lcm(int x,int y){return x / gcd(x, y) * y;}
#define P 1000000007
//#define P 998244353
#define bas 229
inline void ad(int &x, int y){x += y; if(x >= P) x -= P;}
inline void su(int &x, int y){x -= y; if(x < 0) x += P;}
inline void cmn(int &x,int y){if(y < x) x = y;}
inline void cmx(int &x,int y){if(y > x) x = y;}
inline void cmn(LL &x, LL y){if(y < x) x = y;}
inline void cmx(LL &x, LL y){if(y > x) x = y;}
int Pow(int x, int y){
if(y < 0)return Pow(Pow(x, P - 2), -y);
int now = 1 ;
for(;y;y >>= 1, x = 1LL * x * x % P)if(y & 1) now = 1LL * now * x % P;
return now;
}
/*******************************************************************************************************************/
/* */
/*******************************************************************************************************************/
#define N 200005
#define M 10000005
int n, m, f[N], g[N];
vector<int>c[N];
struct Node{
int l, r, val;
}a[M];
#define ls a[x].l
#define rs a[x].r
#define S a[x].val
int idx, rt[N];
void ins(int &x,int l,int r,int pos){
int y = x; x = ++idx, a[x] = a[y];
if(l == r)S = 1;
else{
int mid = (l + r) >> 1;
if(mid >= pos)ins(ls, l, mid, pos);
else ins(rs, mid + 1, r, pos);
S = a[ls].val + a[rs].val;
}
}
int ask(int x,int l,int r,int L,int R){
if(!x)return 0;
if(L >= l && R <= r)return S;
int mid = (L + R) >> 1, sum = 0;
if(mid >= l)sum += ask(ls, l, r, L, mid);
if(mid < r)sum += ask(rs, l, r, mid + 1, R);
return sum;
}
int calc(int x, int y){
if(y != n && ask(rt[x], y + 1, n, 1, n))return f[x] + 0x3f3f3f3f;
return f[x] + y - x - ask(rt[x], x + 1, n, 1, n);
}
struct node{
int x, l, r;
node(int X = 0, int L = 0,int R = 0){x = X, l = L, r = R;}
}q[N];
int hd, tl;
int main(){
//int T = read();while(T--)solve();
n = read(), m = read();
rp(i, m){
int x = read(), y = read();
if(x > y)swap(x, y);
c[x].pb(y);
}
rt[0] = ++idx;
rp(i, n){
rt[i] = rt[i - 1];
go(x, c[i])ins(rt[i], 1, n, x);
}
memset(f, 0x3f, sizeof(f));
f[1] = 0, hd = tl = 1, q[1] = node(1, 2, n);
rep(i, 2, n){
if(q[hd].r == i - 1)hd++;
else q[hd].l++;
f[i] = calc(q[hd].x, i);
while(hd <= tl && calc(i, q[tl].l) <= calc(q[tl].x, q[tl].l))tl--;
if(!tl || calc(q[tl].x, q[tl].r) <= calc(i,q[tl].r)){
if(q[tl].r != n)
q[tl + 1] = node(i, q[tl].r + 1, n), tl++;
}
else{
int l = q[tl].l, r = q[tl].r, ed = 0;
while(l <= r){
int mid = (l + r) >> 1;
if(calc(q[tl].x, mid) <= calc(i, mid))
ed = mid, l = mid + 1;
else r = mid - 1;
}
q[tl].r = ed, q[++tl] = node(i, ed + 1, n);
}
//printf("%d ",f[i]);
}
printf("%d\n", f[n]);
return 0;
}