感覺就是,題並沒有多難,但考場上就是想不出來。
連結: https://vjudge.net/contest/622884#problem/
B
天殺的B題,計算重心,真沒想到
要使得重心儘可能的低,在豎立的時候是不需要考慮的,主要需要考慮水平方向上的,也就是水瓶在空中橫置的時候,這時候就需要用到力矩了。
C
就是在一個圓裡面儘可能的填正方形,其中要求圓心一定是四個相鄰的正方形的公共頂點,直接二分就行了。
一開始直接思維誤區,以為正方形個數是可以直接用 i^2,和 i^2 + 4i 求得的,直接看了5個小時,寄
實則計算出1/4圓能放多少個正方形就夠了,時間複雜度是 nlogn的
D 一個最短路,比較重要的就是建圖了,沒啥必要,ez
E
比較簡單,首先可以想到,對於一個有 n 個頂點,所對應的期望範圍是多少
最小:菊花圖 n - 1 / n = 2(n-1) / 2n
最大: 鏈狀圖,1號頂點在最邊邊上 n * (n - 1) / 2n
那麼對於給定的分數,先化簡成最簡分數的形式,然後分子分母一直乘 2, 找到一個滿足條件的 a,b就好了
然後就是如何建圖,一號點肯定是固定的,接下來就是考慮新的點應該接到哪裡。
考慮二分,二分這個點到一號點的路徑長度mid,得到mid後,考慮剩下的點所能提供的長度最小和最大分別是多少(全都接在一號點上和連成鏈狀,接到離一號點最遠的那個點之後),只要使得還需要的路徑長度之和在最大值和最小值之間就好了,否則就增大mid或者減小mid,並實時更新離一號點最遠的點的距離,l的話就一直是1
摺疊標題
#include<bits/stdc++.h>
const int MAXN = 1e6 + 10;
using namespace std;
typedef long long LL;
int a, b;
int cnt[MAXN];
int nee;
int n, m;
map<int, int> mp;
int gcd(int x, int y)
{
if(!y) return x;
return gcd(y, x % y);
}
int check(int x, int i, int maxn){
int havminn = x + n - i;
int havmaxn = x + (maxn + 1 + maxn + n - i) * (n - i) / 2;
if(havmaxn < nee) return 0;
if(havminn <= nee && havmaxn >= nee) return 1;
if(havminn > nee) return 2;
}
int main()
{
string s;
cin >> s;
int poi = 0;
while(s[poi] != '/') a = a * 10 + s[poi] - '0', poi++;
poi++;
for(int i = poi;i < s.size();i++) b = b * 10 + s[i] - '0';
// cin >> a >> b;
a /= gcd(a, b);
b /= gcd(a, b);
if(a < b - 1) {
cout << "impossible\n";
return 0;
}
// wa 的原因,a 有可能是奇數,所以也要判斷
if(b % 2 || a % 2) b *= 2, a *= 2;
while(b / 2 <= 1e6 && a > (b / 2) * (b / 2 - 1)){
a *= 2;
b *= 2;
}
if(b / 2 > 1e6) {
cout << "impossible\n";
return 0;
}
n = b / 2, m = n - 1;
nee = a / 2;
int l = 1, r = 0;
// int res = 0;
for(int i = 2;i <= n;i++)
{
int L = l, R = r + 1;
while(L < R)
{
int mid = L + R >> 1;
int res = check(mid, i, r);
if(res == 1) {
L = R = mid;
break;
}
else if(res == 0){
L = mid + 1;
}
else R = mid;
}
if(L == r + 1) {
r += 1;
}
nee -= L;
cnt[i] = L;
}
// cnt[n] = nee;
sort(cnt + 1, cnt + n + 1);
cout << n << " " << m << '\n';
mp[0] = 1;
for(int i = 2;i <= n;i++)
{
cout << i << " " << mp[cnt[i]-1] << "\n";
if(!mp[cnt[i]]) mp[cnt[i]] = i;
}
return 0;
}
I 不多說,簽到題
J 排序+線段樹
首先對給定的區間進行排序,開始時間最小,相同的按結束時間最晚排序(感覺這個排序方式很常見,在與區間處理,要求重疊什麼的相關的問題上)
這樣就可以保證在從左往右遍歷的時候,當前的這個區間開始已經是晚於前面的,那麼只需要考慮他們的結束時間就可以了。
兩種做法:
假設所有區間中,最晚結束在時間 cnt
1. 對於當前區間 [L, R],找到 [R, cnt]區間上的最大值,這就是答案
然後再將 [R, cnt] 這個區間更新為最大值+1,
2. 對於當前區間 [L, R],找到 [L, R]區間上的最小值,這就是答案
然後再將[L, R]區間上的每一個數 與 最大值 + 1 作 max 取成最大值操作。
區間修改查詢,線段樹,且需要懶標記
摺疊標題
#include<bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
const int MAXN = 4e5 + 10;
using namespace std;
int n;
struct nodee
{
int id;
int a, b;
}k[MAXN], k2[MAXN];
int ti[MAXN], top;
struct node
{
int l, r;
int val;
int flag;
}t[MAXN<<3];
int ans[MAXN];
void pushdown(int rt)
{
if(t[rt].flag){
t[rt].val = max(t[rt].val, t[rt].flag);
if(t[rt].l != t[rt].r){
// 不能和自己原本的 val 比較,因為你不確定是整個區間最大值都是 這個 val 還是
t[ls].flag = max(t[ls].flag, t[rt].flag);
t[rs].flag = max(t[rs].flag, t[rt].flag);
// 不能確定這裡原本的最大值和下放的flag誰大
t[ls].val = max(t[ls].val, t[ls].flag);
t[rs].val = max(t[rs].val, t[rs].flag);
}
t[rt].flag = 0;
}
return ;
}
void build(int rt, int l, int r)
{
if(l == r)
{
t[rt].l = t[rt].r = l;
t[rt].val = 0;
return ;
}
int mid = l + r >> 1;
t[rt].l = l;
t[rt].r = r;
build(ls, l, mid);
build(rs, mid + 1, r);
return ;
}
int query(int rt, int L, int R)
{
if(t[rt].l > R || t[rt].r < L) return 0;
// return 前下放 必須
pushdown(rt);
if(t[rt].l >= L && t[rt].r <= R) return t[rt].val;
int res = 0;
int mid = t[rt].l + t[rt].r >> 1;
if(L <= mid) res = max(res, query(ls, L, R));
if(R >= mid + 1) res = max(res, query(rs, L, R));
return res;
}
void update(int rt, int L, int R, int val)
{
if(t[rt].l > R || t[rt].r < L) return;
if(t[rt].l >= L && t[rt].r <= R){
t[rt].flag = max(t[rt].flag, val);
t[rt].val = max(t[rt].val, t[rt].flag);
return ;
}
// pushdown 在這裡,和上面都可
pushdown(rt);
update(ls, L, R, val);
update(rs, L, R, val);
// 很重要
t[rt].val = max(t[ls].val, t[rs].val);
return ;
}
void print()
{
for(int i = 1;i <= 20;i++)
{
cout << t[i].l << " " << t[i].r << " " << t[i].val << '\n';
}
cout << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1;i <= n;i++)
{
cin >> k[i].a >> k[i].b;
k[i].b += k[i].a - 1;
k[i].id = i;
ti[++top] = k[i].a;
ti[++top] = k[i].b;
}
sort(ti + 1, ti + 1 + top, [](const int &a, const int &b){
return a < b;
});
top = unique(ti + 1, ti + top + 1) - ti - 1;
sort(k + 1, k + n + 1, [](const nodee &x, const nodee &y){
if(x.a == y.a) return x.b > y.b;
return x.a < y.a;
});
for(int i = 1;i <= n;i++){
k2[i].id = k[i].id;
k2[i].a = lower_bound(ti + 1, ti + top + 1, k[i].a) - ti;
k2[i].b = lower_bound(ti + 1, ti + top + 1, k[i].b) - ti;
}
build(1, 1, top+1);
for(int i = 1;i <= n;i++)
{
int res = query(1, k2[i].b, top + 1);
ans[k2[i].id] = res;
update(1, k2[i].a, k2[i].b, res + 1);
}
for(int i = 1;i <= n;i++) cout << ans[i] << " \n"[i==n];
return 0;
}
摺疊標題
#include<bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
const int MAXN = 4e5 + 10;
using namespace std;
int n;
struct nodee
{
int id;
int a, b;
}k[MAXN], k2[MAXN];
int ti[MAXN], top;
struct node
{
int l, r;
int val;
int flag;
}t[MAXN<<3];
int ans[MAXN];
void pushdown(int rt)
{
if(t[rt].flag){
t[rt].val = max(t[rt].flag, t[rt].val);
if(t[rt].l != t[rt].r){
// 不能和自己原本的 val 比較,因為你不確定是整個區間最大值都是 這個 val 還是
t[ls].flag = max(t[ls].flag, t[rt].flag);
t[rs].flag = max(t[rs].flag, t[rt].flag);
// 不能確定這裡原本的最大值和下放的flag誰大
t[ls].val = max(t[ls].val, t[ls].flag);
t[rs].val = max(t[rs].val, t[rs].flag);
}
t[rt].flag = 0;
}
return ;
}
void build(int rt, int l, int r)
{
if(l == r)
{
t[rt].l = t[rt].r = l;
t[rt].val = 0;
return ;
}
int mid = l + r >> 1;
t[rt].l = l;
t[rt].r = r;
build(ls, l, mid);
build(rs, mid + 1, r);
return ;
}
int query(int rt, int L, int R)
{
if(t[rt].l > R || t[rt].r < L) return 0;
// return 前下放 必須
pushdown(rt);
if(t[rt].l >= L && t[rt].r <= R) return t[rt].val;
int res = MAXN;
int mid = t[rt].l + t[rt].r >> 1;
if(L <= mid) res = min(res, query(ls, L, R));
if(R >= mid + 1) res = min(res, query(rs, L, R));
return res;
}
void update(int rt, int L, int R, int val)
{
if(t[rt].l > R || t[rt].r < L) return;
if(t[rt].l >= L && t[rt].r <= R){
t[rt].flag = max(t[rt].flag, val);
t[rt].val = max(t[rt].val, t[rt].flag);
return ;
}
// pushdown 在這裡,和上面都可
pushdown(rt);
update(ls, L, R, val);
update(rs, L, R, val);
// 很重要
t[rt].val = min(t[ls].val, t[rs].val);
return ;
}
void print()
{
for(int i = 1;i <= 20;i++)
{
cout << t[i].l << " " << t[i].r << " " << t[i].val << '\n';
}
cout << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1;i <= n;i++)
{
cin >> k[i].a >> k[i].b;
k[i].b += k[i].a - 1;
k[i].id = i;
ti[++top] = k[i].a;
ti[++top] = k[i].b;
}
sort(ti + 1, ti + 1 + top, [](const int &a, const int &b){
return a < b;
});
top = unique(ti + 1, ti + top + 1) - ti - 1;
sort(k + 1, k + n + 1, [](const nodee &x, const nodee &y){
if(x.a == y.a) return x.b > y.b;
return x.a < y.a;
});
for(int i = 1;i <= n;i++){
k2[i].id = k[i].id;
k2[i].a = lower_bound(ti + 1, ti + top + 1, k[i].a) - ti;
k2[i].b = lower_bound(ti + 1, ti + top + 1, k[i].b) - ti;
}
build(1, 1, top+1);
for(int i = 1;i <= n;i++)
{
// 最小值
int res = query(1, k2[i].a, k2[i].b);
ans[k2[i].id] = res;
update(1, k2[i].a, k2[i].b, res + 1);
// print();
}
for(int i = 1;i <= n;i++) cout << ans[i] << " \n"[i==n];
return 0;
}