2020CCPC綿陽站 J-Joy of Handcraft (去重 + 線段樹 + 調和級數)

前方77發表於2020-11-08

在這裡插入圖片描述
題意:給定n個燈泡,那個燈泡排列在電路板上,然後每個燈泡給定你一個t和x,這個燈泡會在[2kt + 1,2kt + t],k=(0,1,2…)的時間區間內發出亮度為x的光,其他時間內不發光,然後給定你一個m問你:從第一秒到第m秒每一秒時亮度最大的燈的亮度為多少?

輔助:首先我們應該知道調和級數也就是 (1/1 + 1/2 + 1/3 +…+1/n)這個東西的部分和為ln(n)+ 尤拉常數,近似等於logn

思路:所以這道題對於給定的m個區間,因為求得是最大的的亮度為多少,所以我們先排個序,將重複時間區間的最大的亮度拿出來。這樣再去列舉每個t對應的時間區間是,區間總個數就是(m/1 + m/2 + m/3+…+m/m)也就是mlogm個區間(這就可做了嘛),列舉更新每個區間時間複雜度也就是O(m * logm * logn)的時間複雜度,2s 跑 400ms就過去了。
剩下的就是區間更新的基本操作了。

程式碼:

#include <bits/stdc++.h>
//線段樹的正解做法
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 7;
int n,m;
struct node{
	int t,x;
}b[MAXN],a[MAXN];
bool cmp(node a,node b){
	if(a.t == b.t) return a.x > b.x;//相同時間區間內找出亮度最大的即可
	else return a.t < b.t;
}
int tree[MAXN<<2],lazy[MAXN<<2];
void pushup(int rt) { tree[rt] = max(tree[rt<<1],tree[rt<<1|1]); }
void pushdown(int rt){
	if(lazy[rt]){
		lazy[rt<<1] = max(lazy[rt],lazy[rt<<1]);
		lazy[rt<<1|1] = max(lazy[rt],lazy[rt<<1|1]);
		tree[rt<<1] = max(tree[rt<<1],lazy[rt]);
		tree[rt<<1|1] = max(tree[rt<<1|1],lazy[rt]);
		lazy[rt] = 0;
	}
}
void build(int rt,int l,int r){
	lazy[rt] = 0;
	if(l == r){
		tree[rt] = 0;
		return ;
	}
	int mid = (l+r)>>1;
	build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
	pushup(rt);
}
void modify(int rt,int l,int r,int L,int R,int val){
	if(l >= L && r <= R){
		lazy[rt] = max(lazy[rt],val);
		tree[rt] = max(tree[rt],lazy[rt]);
		return ;
	}
	pushdown(rt);
	int mid = (l+r)>>1;
	if(L <= mid) modify(rt<<1,l,mid,L,R,val);
	if(R > mid) modify(rt<<1|1,mid+1,r,L,R,val);
	pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
	if(l >= L && r <= R) return tree[rt];
	pushdown(rt);
	int ans = 0;int mid = (l+r)>>1;
	if(L <= mid) ans = max(ans,query(rt<<1,l,mid,L,R));
	if(R > mid) ans = max(ans,query(rt<<1|1,mid+1,r,L,R));
	return ans;
}
int main(){
	int T,cas = 0;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i ++){
			scanf("%d%d",&b[i].t,&b[i].x);
		}
		sort(b+1,b+1+n,cmp);
		int cnt = 1;a[1] = b[1];
		for(int i = 2;i <= n;i ++){//按照時間去一下重 把時間大的排在前面
			if(b[i].t != a[cnt].t)
				a[++cnt] = b[i];
		}
		build(1,1,m);
		for(int i = 1;i <= cnt;i ++){//這全部的複雜度為O(m*logm*logn)
			for(int k = 0;;k ++){
				int l = k*2*a[i].t + 1,r = (k*2+1)*a[i].t;
				r = min(r,m);//有些區間的長度可能右端點超過m 取個min
				if(l <= m && r <= m) modify(1,1,m,l,r,a[i].x);
				if(l >= m || r == m) break;
			}
		}
		printf("Case #%d:",++cas);
		for(int i = 1;i <= m;i ++){
			printf(" %d",query(1,1,m,i,i));
		}
		printf("\n");
	}
	return 0;
}

相關文章