Loading

【总结】BalticOI 2012 Day1

2021/7/13

T1 : 括号

第一眼看没有思路,打个暴力。

我们定义 \(f[l][r]\) 表示区间 \([l, r]\) 中的方案数,枚举 \(k\in [l+1, r]\) 表示和第 \(l\) 个括号匹配的位置,得到 \(f[l][r] = \sum f[l + 1][k - 1]\times f[k + 1][r]\)

\(\mathcal{O}(N^3)\) ,注意一下常数可以拿 \(45\) 分。

观察一下发现我们的式子,等价于用一个 \(\texttt{(}\)\(\texttt{)}\) 匹配,或者和 \(\texttt{)}\) 匹配。所以我们只用记录前面有多少个 \(\texttt{(}\) 没有匹配即可。

定义状态 \(f[i][j]\) 表示前面 \(i\) 个位置,还有 \(j\) 个左括号没有匹配的方案,\(\mathcal{O}(N^2)\) 转移一下即可。本来以为有什么 \(\mathcal{O}(N\sqrt N)\) 或者 \(\mathcal{O}(N^{\frac{5}{3}})\) 的神奇做法,没想到 \(N^2\) 直接过了/fad 。

#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define pre(i,a,b) for(register int i=a;i>=b;i--)
#define N 1005
#define P 1000000009
using namespace std;
int n, f[N][N];char s[N];
inline void ad(int &x,int y){x += y; if(x >= P) x -= P;}
int main(){
	scanf("%d", &n);
	scanf("%s", s + 1);
	rep(i, 0, n)f[i + 1][i] = 1;
	pre(i, n, 1)if(s[i] == '('){
		for(int j = i + 1;j <= n; j += 2)
			for(int k = i + 1;k <= j; k += 2)
					ad(f[i][j], 1LL * f[i + 1][k - 1] * f[k + 1][j] % P);
	}
	printf("%d\n", f[1][n]);
	return 0;
}
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define N 30005
#define P 1000000009
using namespace std;
int n, f[2][N];char s[N];
int main(){
	scanf("%d", &n);
	scanf("%s", s + 1);
	f[0][0] = 1;register int k, op = 0;
	rep(i, 1, n){
		k = min(i, n - i);op ^= 1;
		f[op][0] = f[op ^ 1][1];
		rep(j, 1, k){
			if(s[i] == '(')f[op][j] = (f[op ^ 1][j - 1] + f[op ^ 1][j + 1]) % P;
			else f[op][j] = f[op ^ 1][j + 1];
		}
	}
	printf("%d\n", f[op][0]);
	return 0;
}

T2 : 移动网络

一眼看出是二分。

我们直接二分答案,那么每个点一定会 ban 掉线段上的一段,我们再 check 一下这些线段能否完全覆盖 \([0, l]\)

直接对这些区间排序,然后贪心选择即可,这样可以做到 \(\mathcal{O}(N\log^2 N)\) 的时间复杂度。

但是 600ms 显然被卡,应该是有单 \(\log\) 的做法。

我们发现给定的点已经排过序,关键结论就是按照这个顺序贪心一定是对的,写了一发发现过了。

反过来想也不难证明,如果 \(x\) 相同显然不用考虑顺序,如果 \(x\) 不同,讨论一下。如果后面的能覆盖前面的,前面的无论拍在哪都一样,如果不能,那么按照给定顺序一定最优。

时间复杂度 \(\mathcal{O}(N\log \frac{\max{x}}{eps})\)

#include<bits/stdc++.h>
#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 N 1000005
using namespace std;
int n, lim, u[N], v[N];
const double eps = 1e-3;
bool check(double x){
	double cur = 0;
	rep(i, 1, n){
		if(fabs(v[i]) >= x)continue;
		double cen = sqrt(x * x - 1LL * v[i] * v[i]);
		if(u[i] - cen < cur){
			cur = max(cur, u[i] + cen);
			if(cur > lim)return false;
		}
	}
	return true;
}
int main(){
	scanf("%d%d", &n, &lim);
	rep(i, 1, n)scanf("%d%d", &u[i], &v[i]);
	double l = 0, r = 2e9;
	while(r - l >= eps){
		double mid = (l + r) / 2;
		if(check(mid))l = mid;
		else  r = mid;
	}
	printf("%.5lf\n",l);
	return 0;
}

T3 :山峰

想到一个非常神奇的做法。

首先并查集求出每个平台,然后在相邻平台之间连边。

然后求出每个山峰。

对平台从大到小排序,再开一个并查集维护连通性,然后从大到小加点,已经联通的联通块之间的边不加。

这样我们可以得到一颗树,然后我们在树上分治一下。

首先对于每棵子树,求得子树中最大的山峰。

合并的时候,保留最大的山峰,对于其他的比它小的山峰,答案一定是当前点的权值,对于和它相等的山峰,我们将两个山峰合并,表示对于之后子问题这个两个山峰的答案相等。最后合并答案即可。

开始胡了一个直接在重构树上暴跳的做法,想了一下发现可能被卡到平方。

这个做法是线性的,还要算上前面排序和求平台的复杂度。复杂度大概是 \(\mathcal{O}(NM\log)\)

#include<bits/stdc++.h>
#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 N 100005
#define M 2005
using namespace std;
int n, m, h[M][M], fa[N], id[N], idx, u[N], f[N], v[N], ans[N];
const int dx[4] = {0, 1, 1, 1}, dy[4] = {1, -1, 0, 1};
vector<int>e[N], a[N];
inline int g(int x,int y){return (x - 1) * m + y;}
int get(int x){return fa[x] == x ? x : fa[x] = get(fa[x]);}
int gt(int x){return f[x] == x ? x : f[x] = gt(f[x]);}
int ed(int x){return ans[x] >= 0 ? ans[x] : ans[x] = ed(-ans[x]);}
inline void ins(int x,int y){e[x].push_back(y), e[y].push_back(x);}
typedef pair<int,int> Pr;
bool cmp(Pr x, Pr y){return x > y;}
vector<pair<int,int>>c, rk;
int dfs(int x){
	//cout<<"cc "<<x<<endl;
	if(v[x])return x;
	vector<Pr>w;
	for(int y:a[x]){
		int cur = dfs(y);
		if(cur)w.push_back(make_pair(u[cur], cur));
	}
	if(!w.size())return 0;
	sort(w.begin(), w.end(), cmp);
	int cur = w[0].second;
	for(int i = 1; i < (int)w.size();i++)
		if(w[i].first == u[cur])ans[w[i].second] = -cur;
		else{
			ans[w[i].second] = u[x];
		}
	return cur;
}
int main(){
	scanf("%d%d", &n, &m);
	rep(i, 1, n)rep(j, 1, m)scanf("%d", &h[i][j]);
	rep(i, 1, n * m)fa[i] = i;
	rep(i, 1, n)rep(j, 1, m)rep(k, 0, 3){
		int x = i + dx[k], y = j + dy[k];
		if(x < 1 || y < 1 || x > n || y > m)continue;
		if(h[i][j] == h[x][y])fa[get(g(i, j))] = get(g(x, y));
	}
	rep(i, 1, n)rep(j, 1, m)
		if(g(i, j) == fa[g(i, j)])id[g(i, j)] = ++idx, u[idx] = h[i][j];
	rep(i, 1, n)rep(j, 1, m)rep(k, 0, 3){
		int x = i + dx[k], y = j + dy[k];
		if(x < 1 || y < 1 || x > n || y > m)continue;
		if(h[i][j] != h[x][y])ins(id[get(g(i, j))], id[get(g(x, y))]);
	}
	rep(i, 1, idx)c.push_back(make_pair(u[i], i)), f[i] = i;
	sort(c.begin(), c.end(), cmp);
	rep(i, 1, idx){
		v[i] = 1;
		for(int x:e[i])v[i] &= u[i] > u[x];
	}
	//cout<<endl;
	//rep(i, 1, n){rep(j, 1, m)printf("%d ",id[get(g(i, j ))]);putchar('\n');}
	//cout<<endl;
	for(Pr w : c){
		int x = w.second;
		//cout<<"ss "<<x<<" "<<u[x]<<endl;
		for(int y:e[x])if(gt(y) != x && u[gt(y)] >= u[x]){
			//cout<<x<<" "<<gt(y)<<endl;
			a[x].push_back(gt(y));f[gt(y)] = x;
		}
	}
	//rep(i, 1, idx)if(v[i])cout<<i<<" ";cout<<endl;
	//cout<<"ww "<<gt(1)<<endl;
	//cout<<"ww "<<
	dfs(gt(1));//<<endl;
	//rep(i, 1, idx)if(v[i])cout<<"ss "<<i<<" "<<u[i]<<" "<<ans[i]<<endl;
	rep(i, 1, idx)if(v[i])rk.push_back(make_pair(u[i], ed(i)));
	sort(rk.begin(), rk.end(), cmp);
	cout<<rk.size()<<endl;
	for(Pr cur:rk)printf("%d %d\n", cur.first, cur.second);
	//cout<<endl;
	//rep(i, 1, n){rep(j, 1, m)printf("%d ",id[get(g(i, j))]);putchar('\n');}
	//cout<<endl;
	
	return 0;
}
posted @ 2021-07-13 11:40  7KByte  阅读(239)  评论(0编辑  收藏  举报