Loading

【题解】[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;
}

posted @ 2021-08-25 23:25  7KByte  阅读(280)  评论(0编辑  收藏  举报