4.17訓練賽

是谁不可理喻發表於2024-04-19

感覺就是,題並沒有多難,但考場上就是想不出來。

連結: https://vjudge.net/contest/622884#problem/

B

天殺的B題,計算重心,真沒想到

要使得重心儘可能的低,在豎立的時候是不需要考慮的,主要需要考慮水平方向上的,也就是水瓶在空中橫置的時候,這時候就需要用到力矩了。

4.17訓練賽

C

就是在一個圓裡面儘可能的填正方形,其中要求圓心一定是四個相鄰的正方形的公共頂點,直接二分就行了。

一開始直接思維誤區,以為正方形個數是可以直接用 i^2,和 i^2 + 4i 求得的,直接看了5個小時,寄

實則計算出1/4圓能放多少個正方形就夠了,時間複雜度是 nlogn的

4.17訓練賽

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;
}

相關文章