首页 > 其他分享 >洛谷 P8492 - [IOI2022] 无线电信号塔

洛谷 P8492 - [IOI2022] 无线电信号塔

时间:2023-05-08 12:46:52浏览次数:45  
标签:P8492 ch 洛谷 int 线段 mid IOI2022 return find

想到将最优化问题转化为数点问题的一步了,但是因为转化的姿势不太好导致我的数点不太能用特别简洁的数据结构维护,最后只好看题解(

考虑先解决单组询问的问题,对于每个点 \(i\),我们找出它左边最近的 \(h_l\le h_i-D\) 的点 \(l\),和它右边最近的 \(h_r\le h_i-D\) 的点 \(r\),然后新建一条 \([l,r]\) 的线段,那么问题转化为最多能选出的线段条数,使得线段间两两的交不超过 \(1\),再加一。

直接求好像不太能解决 \(D\) 多组询问的问题,因为 \(D\) 一变线段也会变,而线段一变你不论用什么数据结构维护,都需要进行大幅度的修改,这是不好的。因此考虑发掘些性质,容易得到:

  • 任意两个线段要么包含,要么交不超过 \(1\)。

    这个性质告诉我们,最优线段集合的大小就是不包含任何其他线段的线段个数。

  • 对于两个满足 \(h_i>h_j\) 的 \(i,j\),不会出现 \(j\) 对应的线段完全包含于 \(i\) 对应的线段的情况。

    有了这个性质,我们可以得出 \(i\) 对应的线段不会包含其他任何线段的充要条件:\(i\) 对应的线段中不存在任何一个 \(h\) 比它大的点。

这样思路就出来了,先预处理出 \(w_i\) 表示 \(D\) 的最大值使得 \(i\) 对应的线段能够被纳入答案,然后用主席树维护 \(D\) 时候区间内的答案,查询就在主席树上进行区间查询即可。然后有一个小问题就是有可能一个点虽然它在区间内,但是它对应的所有合法都要用覆盖到区间外的点,但是因为性质一的存在,这样的点只可能是最左边的合法点或者最右边的合法点,主席树二分找到它们即可。

时间复杂度 \(n\log n\)。

const int MAXN=1e5;
const int LOG_N=18;
const int INF=0x3f3f3f3f;
const int MAXP=MAXN<<6;
int n,h[MAXN+5],nd[MAXN+5],pre[MAXN+5],nxt[MAXN+5],stk[MAXN+5],tp;
int st[LOG_N+2][MAXN+5],lg[MAXN+5],w[MAXN+5],key[MAXN+5],uni[MAXN+5],num;
vector<int>vec[MAXN+5];
int query_mn(int l,int r){
	if(l>r)return INF;int k=lg[r-l+1];
	return min(st[k][l],st[k][r-(1<<k)+1]);
}
struct node{int ch[2],sum;}s[MAXP+5];
int rt[MAXN+5],ncnt;
void build(int &k,int l,int r){
	k=++ncnt;if(l==r)return;int mid=l+r>>1;
	build(s[k].ch[0],l,mid);build(s[k].ch[1],mid+1,r);
}
int modify(int k,int l,int r,int x){
	int z=++ncnt;s[z]=s[k];s[z].sum++;
	if(l==r)return z;int mid=l+r>>1;
	if(x<=mid)s[z].ch[0]=modify(s[k].ch[0],l,mid,x);
	else s[z].ch[1]=modify(s[k].ch[1],mid+1,r,x);
	return z;
}
int query(int k,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return s[k].sum;int mid=l+r>>1;
	if(qr<=mid)return query(s[k].ch[0],l,mid,ql,qr);
	else if(ql>mid)return query(s[k].ch[1],mid+1,r,ql,qr);
	else return query(s[k].ch[0],l,mid,ql,mid)+query(s[k].ch[1],mid+1,r,mid+1,qr);
}
int find_nxt(int k,int l,int r,int p){
	if(!s[k].sum)return 0;if(l==r)return l;
	int mid=l+r>>1;
	if(p>mid)return find_nxt(s[k].ch[1],mid+1,r,p);
	else{
		int res=find_nxt(s[k].ch[0],l,mid,p);
		if(res)return res;
		else return find_nxt(s[k].ch[1],mid+1,r,p);
	}
}
int find_pre(int k,int l,int r,int p){
	if(!s[k].sum)return 0;if(l==r)return l;
	int mid=l+r>>1;
	if(p<=mid)return find_pre(s[k].ch[0],l,mid,p);
	else{
		int res=find_pre(s[k].ch[1],mid+1,r,p);
		if(res)return res;
		else return find_pre(s[k].ch[0],l,mid,p);
	}
}
void init(int N,vector<int>H){
	n=N;for(int i=0;i<N;i++)h[i+1]=H[i];
	for(int i=1;i<=n;i++){
		while(tp&&h[stk[tp]]<h[i])--tp;
		pre[i]=stk[tp];stk[++tp]=i;
	}stk[tp=0]=n+1;
	for(int i=n;i;i--){
		while(tp&&h[stk[tp]]<h[i])--tp;
		nxt[i]=stk[tp];stk[++tp]=i;
	}
	for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++)st[0][i]=h[i];
	for(int i=1;i<=LOG_N;i++)for(int j=1;j+(1<<i)-1<=n;j++)
		st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
	for(int i=1;i<=n;i++)w[i]=h[i]-max(query_mn(pre[i]+1,i-1),query_mn(i+1,nxt[i]-1));
	for(int i=1;i<=n;i++)key[i]=w[i];sort(key+1,key+n+1);key[0]=-INF;
	for(int i=1;i<=n;i++)if(key[i]!=key[i-1])uni[++num]=key[i];
	for(int i=1;i<=n;i++){
		int pos=lower_bound(uni+1,uni+num+1,w[i])-uni;
		vec[pos].pb(i);
	}
	build(rt[num+1],1,n);
	for(int i=num;i;i--){
		rt[i]=rt[i+1];
		for(int id:vec[i])rt[i]=modify(rt[i],1,n,id);
	}
}
bool check(int l,int r,int x,int D){return h[x]-max(query_mn(max(pre[x]+1,l),x-1),query_mn(x+1,min(nxt[x]-1,r)))>=D;}
int max_towers(int l,int r,int x){
	++l;++r;int p=lower_bound(uni+1,uni+num+1,x)-uni,cnt=query(rt[p],1,n,l,r);
	if(!cnt)return 1;int L=find_nxt(rt[p],1,n,l),R=find_pre(rt[p],1,n,r);
	return cnt-(!check(l,r,L,x))-(L!=R&&!check(l,r,R,x))+1; 
}

标签:P8492,ch,洛谷,int,线段,mid,IOI2022,return,find
From: https://www.cnblogs.com/tzcwk/p/luogu-P8492.html

相关文章

  • 洛谷 P8367 - [LNOI2022] 盒(组合数学)
    设\(a\)数组的前缀和为\(s_i\),\(b\)数组的前缀和为\(t_i\),那么根据模拟费用流或者贪心的思想,每一条边经过的次数即为\(|s_i-t_i|\),因此非常trivial的做法是转换贡献体,枚举每种方案下每条边被经过的次数,然后乘以\(w_i\)求和,具体来说:\[ans=\sum\limits_{i=1}^{n-1}\sum\l......
  • 洛谷 P3369 【模板】普通平衡树
    有旋Treap模板#include<bits/stdc++.h>usingnamespacestd;structNode{ Node*ch[2]; intval,rank; intrep_cnt; intsiz; Node(intval):val(val),rep_cnt(1),siz(1){ ch[0]=ch[1]=nullptr; rank=rand(); } voidupd_siz(){ siz=......
  • 洛谷P4824题解
    题面题意:给出字符串s和t,每次操作将s中出现的第一个t删去,直到不能删为止。求最后的串。|s|<=1e6题解:hash做法。(此题也有kmp和权值线段树做法)因为涉及到删除操作,所以我们要动态的实现这个过程。所以考虑开一个栈来存储当前留下的字符。然后每有一个字符入栈,就拿当前......
  • 洛谷P7469题解
    题面题意:有两个字符串a和b,问b中有多少个本质不同子串可以由a删除若干个字符得到。|a|,|b|<=3000题解:字典树(这个题做法很多,后补)。把字符串b的每个子串打到字典树上。然后因为3000^2*26这个东西比较大,所以不能用nxt[id][26]来存储,于是使用链式前向星存图,这样tri......
  • 洛谷 P6938 - [ICPC2017 WF]Son of Pipe Stream(网络流)
    见过的最怪的网络流题,没有之一。首先新建超级源点,向\(1,2\)各连\(\infty\)的边。设最大流为\(A\),那么显然最优方案中flutter和water流量之和为\(A\)。先分析一波答案函数。显然,最终答案关于flutter的流量\(x\)的函数\(f(x)=x^a(A-x)^{1-a}\)。求导得\(f'(x)=ax^......
  • 洛谷 P7579 「RdOI R2」称重(weigh) 题解
    题意:题目一道交互题。有n个球,里面有两个假球,假球比普通球的要轻,每次可以询问任意两组球的轻重关系,第一组轻为<,第二组轻为>,一样重量为=。思路:先考虑在一个区间内找1个假球。我们可以将区间尽量平均分为3份,先询问相等的两组球的轻重关系,分3种情况:第一组轻......
  • 【做题笔记】洛谷 P7987 [USACO21DEC] Paired Up G
    在我的个人博客获得更好的阅读体验Problem洛谷P7987[USACO21DEC]PairedUpG题目大意:有\(n\)个点,其中第\(i\)个点位置为\(x_i\),权值为\(y_i\)。若两个点\(i,j\)满足\(|x_i-x_j|\lek\),则这两个点之间有一条边。求一个匹配,在满足其为极大匹配的情况下匹配点的......
  • 洛谷 P3374——树状数组 / 树状数组模板题
    洛谷P3374——树状数组#include<iostream>usingnamespacestd;constintN=5e5+10;inttr[N],a[N];intn,m;intlowbit(intx){returnx&-x;}voidadd(intu,intc){//给所有管辖中有a[i]的tr[]加上cfor(inti=u;i<=n;i+......
  • 洛谷P2241 统计方形 ,棋盘问题升级板,给出格子坐标中矩形以及正方形的计算方法
    在做这道题之前我们先了解一下棋盘问题棋盘问题(qq.com)......
  • 洛谷:P5716日份天数
    题目描述输入年份和月份,输出这一年的这一月有多少天。需要考虑闰年。输入格式输入两个正整数,分别表示年份\(y\)和月数\(m\),以空格隔开。输出格式输出一行一个正整数,表示这个月有多少天。样例#1样例输入#119268样例输出#131样例输入#220002样例输出#229......