2024/12/19 总结

Recommendations

给你 n 个区间,对每个区间求所有包含这个区间的区间包含元素的交(不包括被包含的区间本身)

将l设为x轴,r设为y轴,每个点就可以表示为平面直角坐标系上的点。发现一个点的左上所有点被这个点包含,从上往下扫描线,从左往右加点即可。需要离散化,特判区间重合

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define _1 first
#define _2 second
constexpr int maxn = 1e6+10;
constexpr int inf = 1e18;
int n , m , l[maxn] , r[maxn] , a[maxn];
pair <int , int> lst;
 
vector <pair <int , int> > p[maxn];
int mn[maxn] , mx[maxn] , ans[maxn];
void update1 (int pos , int c){
	while (pos <= m){
		mn[pos] = min (c , mn[pos]);
		pos += pos & (-pos);
	}
}
int query1 (int pos){
	int ans = inf;
	while (pos){
		ans = min (ans , mn[pos]);
		pos -= pos & (-pos);
	}
	return ans;
}
void update2 (int pos , int c){
	while (pos <= m){
		mx[pos] = max (c , mx[pos]);
		pos += pos & (-pos);
	}
}
int query2 (int pos){
	int ans = 0;
	while (pos){
		ans = max (ans , mx[pos]);
		pos -= pos & (-pos);
	}
	return ans;
}
signed main (){
	ios::sync_with_stdio (0);
    cin.tie(0),cout.tie(0);
	int t;
	cin >> t;
	while (t --> 0){
		m = 0;
		cin >> n;
		for (int i = 1;i <= n;i ++)
			cin >> l[i] >> r[i] , a[++ m] = l[i] , a[++ m] = r[i] , ans[i] = 0;
		sort (a + 1 , a + m + 1);
		m = unique (a + 1 , a + m + 1) - a - 1;
		for (int i = 1;i <= n;i ++){
			l[i] = lower_bound (a + 1 , a + m + 1 , l[i]) - a , r[i] = lower_bound (a + 1 , a + m + 1 , r[i]) - a;
			p[r[i]].push_back ({l[i] , i});
		}
		for (int i = 1;i <= m;i ++)
			sort (p[i].begin () , p[i].end ()) , mx[i] = 0 , mn[i] = 1e18;
		for (int i = m;i >= 1;i --){
			lst = {0 , 0};
			for (auto j : p[i]){
				if (lst._1 == j._1)
					ans[lst._2] = 0;
				else{
					int f = query1 (j._1) , g = query2 (j._1);
					if (g >= 1 && f <= m)
						ans[j._2] = a[query1 (j._1)] - a[query2 (j._1)];
					update1 (j._1 , i) , update2 (j._1 , j._1);
				}
				lst = j;
			}
			p[i].clear ();
		}
		for (int i = 1;i <= n;i ++)
			cout << max (0ll , ans[i] - a[r[i]] + a[l[i]]) <<endl;
	}
	return 0;
}

T-shirts

n种物品,有质量q和价格c。m个顾客,每个顾客每次会选当前能负担起的最大价值物品,价值相同时选最便宜的,选若干次直到没有能选的了。每个物品对每个顾客限购一次。

首先不妨将问题转换一下,直接打暴力的话是对每个顾客枚举物品,显然无法优化。所以变为先将物品按优先级排序,显然从最高优先级的开始枚举,能买得起的顾客都会买,同时买之后将所有买过的顾客答案增加,余额减少。

建一颗平衡树来进行优化,但是我们发现依旧无法避免暴力修改顾客的答案和余额,所以有一个技巧。

我们枚举到一个物品时,将树(用的FHQ,比较好写)分裂成剩余价格为0c,c2c,2c~ 三部分。显然第一部分买不起这个物品,第二部分进去暴力修改,第三部分由于修改之后位置不会变,所以直接打一个答案标记和余额标记就可以了。

#include<bits/stdc++.h>
using namespace std;
constexpr int maxn = 2e5+10;
 
int n, m;
struct ASK{
	int c,q;
	bool operator <(const ASK x){
		if(q != x.q){
			return q > x.q;
		}
		return c < x.c;
	}
}a[maxn];
mt19937 Rand(time(0));
struct note{
	int l,r;
	int id, val, key;
	int cntlz,minlz,cnt;
}s[maxn];
int ncnt = 0;
int root = 0;
 
void push_down(int k) {
	if (s[k].minlz) {
		if (s[k].l) s[s[k].l].minlz += s[k].minlz,
			s[s[k].l].val -= s[k].minlz;
		if (s[k].r) s[s[k].r].minlz += s[k].minlz,
			s[s[k].r].val -= s[k].minlz;
		s[k].minlz = 0;
	}
	if (s[k].cntlz) {
		if (s[k].l) s[s[k].l].cntlz += s[k].cntlz, s[s[k].l].cnt += s[k].cntlz;
		if (s[k].r) s[s[k].r].cntlz += s[k].cntlz, s[s[k].r].cnt += s[k].cntlz;
		s[k].cntlz = 0;
	}
}
 
void split(int k,int val,int &x,int &y){
	if(!k){
		x = y = 0;
		return;
	}
	push_down(k);
	if(s[k].val <= val){
		x = k;
		split(s[k].r,val,s[k].r,y);
	}
	else{
		y = k;
		split(s[k].l,val,x,s[k].l);
	}
}
 
int merge(int x,int y){
	if(!x || !y) return x | y;
	push_down(x),push_down(y);
	if(s[x].key < s[y].key){
		s[x].r = merge(s[x].r,y);
		return x;
	}
	else{
		s[y].l = merge(x,s[y].l);
		return y;
	}
}
void suball(int x,int &y,int z){
	if(!x) return;
	push_down(x);
	suball(s[x].l,y,z);
	suball(s[x].r,y,z);
	s[x].l = s[x].r = 0;
	s[x].val -= z,s[x].cnt++;
	
	int rt1,rt2;
	split(y,s[x].val,rt1,rt2);
	y = merge(merge(rt1,x),rt2);
}
 
void insert(int x,int id){
	int rt1,rt2;
	split(root,x,rt1,rt2);
	ncnt++;
	s[ncnt].id = id;
	s[ncnt].key = Rand();
	s[ncnt].val = x;
	root = merge(merge(rt1,ncnt),rt2);
}
 
int ans[maxn];
void query(int x){
	if(!x) return;
	push_down(x);
	ans[s[x].id] = s[x].cnt;
	query(s[x].l) , query(s[x].r);
}
int main(){
	cin.tie(0) -> sync_with_stdio(0);
	cin>>n;
	for(int i = 1;i <= n;i++){
		cin>>a[i].c>>a[i].q;
	}
	sort(a+1,a+n+1);
	cin>>m;
	for(int i = 1;i <= m;i++){
		int x;
		cin>>x;
		insert(x,i);
	}
	for(int i = 1;i <= n;i++){
		int rt1,rt2,rt3;
		split(root,a[i].c-1,rt1,rt2);
		split(rt2,a[i].c<<1,rt2,rt3);
		if(rt3){
			s[rt3].val -= a[i].c;
			s[rt3].cnt++;
			s[rt3].minlz += a[i].c;
			s[rt3].cntlz++;
		}
		suball(rt2,rt1,a[i].c);
		root = merge(rt1,rt3);
	}
	for(int i = 1;i <= m;i++){
		ans[s[i].id] = s[i].cnt;
	}
	query(root);
	for(int i = 1;i <= m;i++){
		cout<<ans[i]<<" ";
	}
	return 0;
}