【总结】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;
}