牛客练习赛70之F曲调

是个很有趣的题目,当时比赛的时候我是并没有写出来的

看到赛后出题者发的题解一步一步敲,交了足足几十发

这个是题解

官方题解

这是个最后在队友的debug之下才能ac的弱鸡代码,害,可谓失之毫厘,差之千里了。

写这篇博客的用意也没有什么,只是看到题解虽然写的是主席树解法,但是过的几位巨佬大多数是用线段树过的而且非常快 ,所以发个主席树的写法

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+20;
const int mod = 1000000007;

int T;
int n,m,q;
ll a[maxn],ans[maxn*3];
vector<pair<int,int>>v[maxn];
vector<ll>b;

//树状数组维护最小值
//因为维护的是l的最小值,在使用树状数组的时候倒着来 
ll f[maxn];
inline int lowbit(int x) {
	return x&(-x);
}
void change(int x, ll val){
    for (; x <= n; x += lowbit(x)) f[x] = min(f[x], val);
}
ll find(int x) {
	ll res = (1ll << 60);
	for(; x ; x -= lowbit(x)) res = min(f[x],res);
	return res;
} 
//zhu_xi_shu
struct Tree{
	int l,r;
	int sum;
}tree[maxn*20];
int root[maxn],cnt(0),sz(0);		//根标号,根数,数值大小; 
int update(int t,int st,int L,int R) {
	int nowp = ++cnt;		//
	tree[nowp].l = tree[st].l; tree[nowp].r = tree[st].r;
	tree[nowp].sum = tree[st].sum+1;
	if(L == R) return nowp;
	int mid = (L+R)>>1;
	if(mid >= t) tree[nowp].l = update(t,tree[nowp].l,L,mid);
	else tree[nowp].r = update(t,tree[nowp].r,mid+1,R);
	return nowp;
}
int query(int l,int r,int k,int L,int R){
	if(L == R) return L;
	int sum = tree[tree[r].l].sum - tree[tree[l].l].sum;
	int mid = (L+R)>>1;
	if(k <= sum) return query(tree[l].l,tree[r].l,k,L,mid);
	else return query(tree[l].r,tree[r].r,k - sum,mid+1,R);
}
//*******************************
int check(int l,int r,int k1,int k2,int L,int R) {					//主席树区间查询
	int sum = tree[r].sum - tree[l].sum;
	if(k1 <= L && k2 >= R) return sum;

	int mid = (L+R)>>1;
	if(k2 <= mid) {
		return check(tree[l].l,tree[r].l,k1,k2,L,mid);
	} else if(k1 > mid){
		return check(tree[l].r,tree[r].r,k1,k2,mid+1,R);
	} else {
		return (check(tree[l].l,tree[r].l,k1,mid,L,mid) + check(tree[l].r,tree[r].r,mid+1,k2,mid+1,R));
	}
}
int findmx(int l,int r,int k1,int k2) {										//二分查询
	int res(0);
	if(k1 > k2) return res;
	while(l <= r) {
		int mid = (l+r)>>1;
 		if(check(root[mid-1],root[r],k1,k2,1,sz)) {
			res = mid;
			l = mid+1;
		} else {
			r = mid-1;
		}
	}
	return res;
}
int t,temp,x;	//**************************
int main(){
    #ifdef LOCAL
		freopen("test1.txt","r",stdin);
	//	freopen("test2.txt","w",stdout);
	#endif
//	scanf("%d",&T);
	T = 1;
	for(int T1 = 1; T1 <= T; T1++){
		scanf("%d %d",&n,&q);
		for(int i = 1; i <= n; i++) {
			scanf("%lld",a+i);
			a[i] += a[i-1];				//前缀和
			b.push_back(a[i]);
			f[i] = (1ll << 60);
		}
		//**********建主席树************* 
		sort(b.begin(),b.end());
		b.erase(unique(b.begin(),b.end()),b.end());		//离散化
		sz = b.size();
		for(int i = 1; i <= n; i++) {
			t = lower_bound(b.begin(),b.end(),a[i]) - b.begin() + 1;
			root[i] = update(t,root[i-1],1,sz);
		}
		//**********离线询问************ 
		for(int i = 1; i <= q; i++) {
			int l,r; scanf("%d %d",&l,&r);
			v[r].push_back(make_pair(l,i));
		}
		//*******右端点升序查询********** 
		for(int i = 1; i <= n; i++) {					//按照题解思路照搬着写的
			change(n-i+1,abs(a[i]-a[i-1]));
			t = lower_bound(b.begin(),b.end(),a[i]) - b.begin() + 1;
			for(x = findmx(1,i-1,1,t); x && x != 1 && a[x] != a[i]; x = findmx(1,x-1,temp,t) ) {
				temp = lower_bound(b.begin(),b.end(),(a[x]+a[i])/2) - b.begin() + 1;
				temp = max(1,temp-1);
				change(n-x,abs(a[i]-a[x]));
			}
			 change(n-x,abs(a[i]-a[x]));
			
			for(x = findmx(1,i-1,t,sz); x && x != 1 && a[x] != a[i]; x = findmx(1,x-1,t,temp)) {
				temp = lower_bound(b.begin(),b.end(),(a[x]+a[i])/2) - b.begin() + 1;
				change(n-x,abs(a[i]-a[x]));
			}
			change(n-x,abs(a[i]-a[x]));
			for(int j = 0; j < v[i].size(); j++) {
				int l = v[i][j].first, id = v[i][j].second;
				ans[id] = find(n-l+1);
			}
		}
		for(int i = 1; i <= q; i++) {
			printf("%lld\n",ans[i]);
		}
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值