HGOI 20200727
回归水题大赛
T1 圆排列(heightround)
显然最优的排列就是从最小的到最大的的两条上升路径串起来
考虑二分答案
对于一个答案t
我们每次将距离最接近t的点加入,这样可以获得第一个上升路径的最差情况
然后判断剩余的点能否构成上升路径
注意到 \(check\) 得到的第一个路径其实就是答案的后面的路径,这样也可以方便的得出答案
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
const int N = 1010 ;
int n, Ans ;
int a[N], vis[N], ans[N] ;
bool check(int stp) {
memset(vis, 0, sizeof(vis)) ;
int p = 1 ;
rep(i, 2, n)
if (a[i] - a[p] > stp) {
if (i == p + 1) return false ;
vis[i - 1] = 1 ;
p = i - 1 ;
}
if (a[n] - a[p] > stp) return false ;
p = 1 ;
rep(i, 2, n - 1)
if (!vis[i]) {
if (a[i] - a[p] > stp) return false ;
p = i ;
}
if (a[n] - a[p] > stp) return false ;
return true ;
}
void getans(int stp) {
memset(vis, 0, sizeof(vis)) ;
int p = 1, id = 0 ;
rep(i, 2, n)
if (a[i] - a[p] > stp) {
vis[i - 1] = ++id ;
p = i - 1 ;
}
vis[n] = ++id ;
// rep(i, 1, n) cout << vis[i] << " " ; cout << endl ;
p = n ;
per(i, n - 1, 2)
if (!vis[i]) {
vis[i] = ++id ;
p = i ;
}
vis[1] = ++id ;
rep(i, 1, n) ans[vis[i]] = a[i] ;
reverse(ans + 1, ans + n + 1) ;
rep(i, 1, n) printf("%d ", ans[i]) ; cout << endl ;
}
signed main() {
// freopen("heightround.in", "r", stdin) ;
// freopen("heightround.out", "w", stdout) ;
int t ; scanf("%d", &t) ;
while (t--) {
scanf("%d", &n) ;
rep(i, 1, n) scanf("%d", &a[i]) ;
sort(a + 1, a + n + 1) ;
int l = 0, r = a[n] - a[1] ;
Ans = r + 10 ;
while (l <= r) {
int mid = (l + r) >> 1 ;
if (check(mid)) Ans = mid, r = mid - 1 ;
else l = mid + 1 ;
}
// cout << Ans << endl ;
getans(Ans) ;
}
return 0 ;
}
T2 彩色(color)
这是一个没什么含金量的题目
因为 \(N\) 只有50,考虑暴力作法(可以扫描线+线段树做的)
首先离散化,然后从后往前记录每一个矩形的获得染色面积
只需要 \(check\) 每一个小的离散化矩形有没有被占用即可
然后 \(sort\) 找出前 \(k\) 大取出即可
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
#define x1 __XXX1___
#define x2 __XXX2___
#define y1 __YYY1___
#define y2 __YYY2___
const int N = 1010 ;
struct node {
int x1, y1, x2, y2 ;
} a[N] ;
int n, k ;
vector <int> xset, yset ;
vector <pair<int, int> > ans ;
int aid[N], filled[N][N] ;
int S(node a) {
return (xset[a.x2] - xset[a.x1]) * (yset[a.y2] - yset[a.y1]) ;
}
bool cmp(pair <int, int> a, pair <int, int> b) {
if (a.fi != b.fi) return a.fi > b.fi ;
else return a.se < b.se ;
}
signed main() {
// freopen("color.in", "r", stdin) ;
// freopen("color.out", "w", stdout) ;
scanf("%d%d", &n, &k) ;
rep(i, 1, n) {
scanf("%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2) ;
xset.pb(a[i].x1) ; xset.pb(a[i].x2) ;
yset.pb(a[i].y1) ; yset.pb(a[i].y2) ;
}
sort(xset.begin(), xset.end()) ;
sort(yset.begin(), yset.end()) ;
xset.erase(unique(xset.begin(), xset.end()), xset.end()) ;
yset.erase(unique(yset.begin(), yset.end()), yset.end()) ;
rep(i, 1, n) {
a[i].x1 = lower_bound(xset.begin(), xset.end(), a[i].x1) - xset.begin() ;
a[i].x2 = lower_bound(xset.begin(), xset.end(), a[i].x2) - xset.begin() ;
a[i].y1 = lower_bound(yset.begin(), yset.end(), a[i].y1) - yset.begin() ;
a[i].y2 = lower_bound(yset.begin(), yset.end(), a[i].y2) - yset.begin() ;
}
// for (int i = 0; i < xset.size(); i++) printf("%d ", xset[i]) ; cout << endl ;
// for (int i = 0; i < yset.size(); i++) printf("%d ", yset[i]) ; cout << endl ;
// rep(i, 1, n) printf("%d %d %d %d\n", a[i].x1, a[i].x2, a[i].y1, a[i].y2) ;
ans.resize(n) ;
per(i, n, 1) {
ans[i - 1].se = i - 1 ;
rep(j, a[i].x1 + 1, a[i].x2)
rep(k, a[i].y1 + 1, a[i].y2) {
if (!filled[j][k]) ans[i - 1].fi += S((node) {j - 1, k - 1, j, k}) ;
filled[j][k] = true ;
}
}
// rep(i, 0, siz(ans) - 1) cout << ans[i].fi << " " << ans[i].se << endl ;
sort(ans.begin(), ans.end(), cmp) ;
rep(i, 0, k - 1) aid[i + 1] = ans[i].se ;
sort(aid + 1, aid + k + 1) ;
rep(i, 1, k) printf("%d ", aid[i]) ; cout << endl ;
return 0 ;
}
T3 联络(biu)
这也是一个很简单的题目
题目意思是构建补图,然后在同一个连通块的放在同一个集合里
考虑 \(m\) 只有 \(2000000=(1000)*(2000)/2\),因此成为完全图只会有 \(2000\) 个点左右
也就意味着 \(friend\) 最少的点的 \(friend\) 最多两千左右
这样我们就可以把最少的点和没有和他一起的点合并
然后在按照 \(O(N^2)\) 的方法做
\(n^2\) 就是每次加入一个点,看能否更新一些点和他在一起,用并查集维护
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define siz(a) a.size()
using namespace std;
const int N = 100010 ;
int n, m, ans, cnt, num;
int map[3010][3010], vis[N], degree[N], pool[N];
int father[N], p[N], hash1[20010];
vector<int> ch[N];
int get_father(int x) {
if (x == father[x])
return x;
father[x] = get_father(father[x]);
return father[x];
}
void merge(int x, int y) {
x = get_father(x);
y = get_father(y);
if (x != y)
father[y] = x;
}
bool is_num(char ch) { return ch >= '0' && ch <= '9'; }
void read(int &x) {
int tmp = 0;
char ch = getchar();
while (!is_num(ch)) ch = getchar();
while (is_num(ch)) {
tmp *= 10;
tmp += ch - '0';
ch = getchar();
}
x = tmp;
}
int main() {
scanf("%d%d", &n, &m);
int a, b, k;
rep(i, 1, n) father[i] = i;
rep(i, 1, m) {
int a, b ; scanf("%d%d", &a, &b) ;
ch[a].push_back(b);
ch[b].push_back(a);
degree[a]++;
degree[b]++;
}
k = 1;
rep(i, 2, n) if (degree[i] < degree[k]) k = i ;
int t = ch[k].size();
cnt = 1;
rep(i, 0, siz(ch[k]) - 1) vis[ch[k][i]] = 1 ;
rep(i, 1, n)
if (!vis[i]) {
num++;
pool[i] = 1;
} else
pool[i] = ++cnt;
rep(i, 1, n)
rep(j, 0, siz(ch[i]) - 1)
if (pool[i] != pool[ch[i][j]]) map[pool[i]][pool[ch[i][j]]]++;
rep(i, 1, cnt)
rep(j, 1, cnt)
if (i != j) {
if (j == 1 && map[i][j] < num) merge(j, i);
if (j != 1 && !map[i][j]) merge(i, j);
}
rep(i, 1, cnt) p[get_father(i)]++;
p[1] += num - 1;
rep(i, 1, cnt) if (p[i]) hash1[++ans] = p[i];
printf("%d\n", ans);
sort(hash1 + 1, hash1 + ans + 1);
rep(i, 1, ans - 1) printf("%d ", hash1[i]);
printf("%d\n", hash1[ans]);
return 0;
}
加油ヾ(◍°∇°◍)ノ゙