hiho 1169(猜数字-线段树求min(|Ai-k|))

#1169 : 猜数字

时间限制:10000ms

单点时限:5000ms

内存限制:256MB

描述

你正在和小冰玩一个猜数字的游戏。小冰首先生成一个长为N的整数序列A1, A2,
…, AN。在每一轮游戏中,小冰会给出一个区间范围[L, R],然后你要猜一个数K。如果K在AL,
AL+1, …, AR中,那么你获胜。

在尝试了几轮之后,你发现这个游戏太难(无聊)了。小冰决定给你一些提示,你每猜一次,小冰会告诉你K与AL, AL+1,
…, AR中最接近的数的绝对差值,即min(|Ai - K|), L
≤ i ≤ R。

现在,请你实现这个新功能。

输入

第一行为一个整数T,表示数据组数。

每组数据的第一行为两个整数N和Q。

第二行为N个由空格分开的整数,分别代表A1, A2,
…, AN。

接下来Q行,每行三个由空格隔开的整数L、R、K。

输出

每组数据的先输出一行"Case #X:",X为测试数据编号。

接下来对每个询问输出一行,每行为一个整数,即为所求的值。

数据范围

1 ≤ T ≤ 20

0 ≤ Ai, K ≤ 109

1 ≤ L ≤ R ≤ N

小数据

1 ≤ N, Q ≤ 1000

大数据

1 ≤ N, Q ≤ 200000

输入数据量较大,推荐使用scanf / BufferedReader等IO方法。

样例输入
1
9 3
1 8 3 4 9 2 7 6 5
1 9 10
3 7 9
5 6 5
样例输出
Case #1:
1
0
3

EmacsNormalVim

把a[i],和询问k离线,从小到大排序,保证询问k时只插入≤k的

同理>k

注意set操作可能为-INF,_min不能初始化为-1,而要为-INF

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEM2(a,i) memset(a,i,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define MAXN (200000+10)
#define MAXQ (200000+10)

typedef long long ll;
int n,q;
ll a[MAXN];
class SegmentTree
{
    ll a[MAXN*4],minv[MAXN*4],sumv[MAXN*4],maxv[MAXN*4],addv[MAXN*4],setv[MAXN*4];
    int n;
public:
    SegmentTree(){MEM(a) MEM(minv) MEM(sumv) MEM(maxv) MEM(addv) MEM2(setv,-1) }
    SegmentTree(int _n):n(_n){MEM(a) MEM(minv) MEM(sumv) MEM(maxv) MEM(addv) MEM2(setv,-1) }
    void mem(int _n)
    {
        n=_n;
        MEM(a) MEM(minv)  MEM(sumv) MEM(maxv) MEM(addv) MEM2(setv,-1)
    }  

    void maintain(int o,int L,int R)
    {

		sumv[o]=maxv[o]=minv[o]=0;
    	if (L<R) //只考虑左右子树
		{
			sumv[o]=sumv[Lson]+sumv[Rson];
			minv[o]=min(minv[Lson],minv[Rson]);
			maxv[o]=max(maxv[Lson],maxv[Rson]);
		} //只考虑add操作
		if (setv[o]!=-1) sumv[o]=setv[o]*(R-L+1),minv[o]=maxv[o]=setv[o];

		minv[o]+=addv[o];maxv[o]+=addv[o];sumv[o]+=addv[o]*(R-L+1);
    }

	int y1,y2,v;
	void update(int o,int L,int R) //y1,y2,v
	{
		if (y1<=L&&R<=y2) {
			addv[o]+=v;
		}
		else{
			pushdown(o);
			int M=(R+L)>>1;
			if (y1<=M) update(Lson,L,M); else maintain(Lson,L,M);
			if (M< y2) update(Rson,M+1,R); else maintain(Rson,M+1,R);
		}

		maintain(o,L,R); 

	}
	void update2(int o,int L,int R)
	{
		if (y1<=L&&R<=y2) {
			setv[o]=v;addv[o]=0;
		}
		else{
			pushdown(o);
			int M=(R+L)>>1;
			if (y1<=M) update2(Lson,L,M); else maintain(Lson,L,M); //维护pushodown,再次maintain
			if (M< y2) update2(Rson,M+1,R); else maintain(Rson,M+1,R);
		}

		maintain(o,L,R);
	}

	void pushdown(int o)
	{
		if (setv[o]!=-1)
		{
			setv[Lson]=setv[Rson]=setv[o];
			addv[Lson]=addv[Rson]=0;
			setv[o]=-1;
		}
		if (addv[o])
		{
			addv[Lson]+=addv[o];
			addv[Rson]+=addv[o];
			addv[o]=0;
		}
	}
	ll _min,_max,_sum; 

	void query2(int o,int L,int R,ll add)
	{
		if (setv[o]!=-1)
		{
			_sum+=(setv[o]+addv[o]+add)*(min(R,y2)-max(L,y1)+1);
			_min=min(_min,setv[o]+addv[o]+add);
			_max=max(_max,setv[o]+addv[o]+add);
		} else if (y1<=L&&R<=y2)
		{
			_sum+=sumv[o]+add*(R-L+1);
			_min=min(_min,minv[o]+add);
			_max=max(_max,maxv[o]+add);
		} else {
		//	pushdown(o);
			int M=(L+R)>>1;
			if (y1<=M) query2(Lson,L,M,add+addv[o]);// else maintain(Lson,L,M);
			if (M< y2) query2(Rson,M+1,R,add+addv[o]);// else maintain(Rson,M+1,R);
		}
		//maintain(o,L,R);
	}

	void add(int l,int r,ll v)
	{
		y1=l,y2=r;this->v=v;
		update(1,1,n);
	}
	void set(int l,int r,ll v)
	{
		y1=l,y2=r;this->v=v;
		update2(1,1,n);
	}
	ll ask(int l,int r,int b=0)
	{
		_sum=0,_min=INF,_max=-INF;
		y1=l,y2=r;
		query2(1,1,n,0);
	//	cout<<_sum<<' '<<_max<<' '<<_min<<endl; 

		switch(b)
		{
			case 1:return _sum;
			case 2:return _min;
			case 3:return _max;
			default:break;
		}
	}
	void print()
	{
		For(i,n)
			cout<<ask(i,i,1)<<' ';
		cout<<endl;

	}

    //先set后add
}S;
struct ask
{
	ask(int _l=0,int _r=0,ll _k=0,int _i=0){l=_l,r=_r,k=_k;i=_i;}
	int l,r,i;
	ll k;
	friend bool operator<(ask a,ask b){return (a.k<b.k)||(a.k==b.k&&a.l<b.l);	}
}comm[MAXN+MAXQ];
ll ans1[MAXQ],ans2[MAXQ],qur[MAXQ];
int main()
{
//	freopen("B.in","r",stdin);
//	freopen(".out","w",stdout);
	int T;
	scanf("%d",&T);
	For(kcase,T)
	{
		cin>>n>>q;
		For(i,n) scanf("%lld",&a[i]);
		For(i,n)
		{
			comm[i]=ask(0,0,a[i]);
			comm[i].i=i;
		}
		printf("Case #%d:\n",kcase);		

		For(i,q)
		{
			scanf("%d%d%lld",&comm[n+i].l,&comm[n+i].r,&comm[n+i].k);
			qur[i]=comm[n+i].k;
			comm[n+i].i=i;
		}

		int m=n+q;
		sort(comm+1,comm+1+m);

		S.mem(n);
		S.set(1,n,-INF);

		For(i,m)
		{
			if (!comm[i].l) S.set(comm[i].i,comm[i].i,comm[i].k);
			else
			{
				ans1[comm[i].i]=S.ask(comm[i].l,comm[i].r,3);
			}
		}

		S.mem(n);
		S.set(1,n,INF);

		ForD(i,m)
		{
			if (!comm[i].l) S.set(comm[i].i,comm[i].i,comm[i].k);
			else
			{
				ans2[comm[i].i]=S.ask(comm[i].l,comm[i].r,2);
			}
		}

		ll ans=0;

		For(i,q) printf("%lld\n",min(abs(ans1[i]-qur[i]),abs(ans2[i]-qur[i])));
	}

	return 0;
}
时间: 2024-10-10 16:42:56

hiho 1169(猜数字-线段树求min(|Ai-k|))的相关文章

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

hihocoder 1169 猜数字

传送门 时间限制:10000ms 单点时限:5000ms 内存限制:256MB 描述 你正在和小冰玩一个猜数字的游戏.小冰首先生成一个长为N的整数序列A1, A2, …, AN.在每一轮游戏中,小冰会给出一个区间范围[L, R],然后你要猜一个数K.如果K在AL, AL+1, …, AR中,那么你获胜. 在尝试了几轮之后,你发现这个游戏太难(无聊)了.小冰决定给你一些提示,你每猜一次,小冰会告诉你K与AL, AL+1, …, AR中最接近的数的绝对差值,即min(|Ai - K|), L ≤ i

线段树求逆序数方法 HDU1394&amp;&amp;POJ2299

为什么线段树可以求逆序数? 给一个简单的序列 9 5 3 他的逆序数是3 首先要求一个逆序数有两种方式:可以从头开始往后找比当前元素小的值,也可以从后往前找比当前元素大的值,有几个逆序数就是几. 线段树就是应用从后往前找较大值得个数.(一边更新一边查) 当前个数是 n = 10 元素   9  5   3 9先加入线段树,T[9]+=1:查从T[9]到T[10]比9大的值,没有sum = 0: 5 加入线段树,T[5] += 1,查从T[5]到T[10]比5大的值,有一个9,sum +=1: 3

2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)

原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthesis sequence P=p1 p2…pn of length n and q questions. The i-th question is whether P remains balanced after pai and pbi  swapped. Note that questions ar

HDU 1394 Minimum Inversion Number(线段树求逆序数)

题目地址:HDU 1394 这题可以用线段树来求逆序数. 这题的维护信息为每个数是否已经出现.每次输入后,都从该点的值到n-1进行查询,每次发现出现了一个数,由于是从该数的后面开始找的,这个数肯定是比该数大的.那就是一对逆序数,然后逆序数+1.最后求完所有的逆序数之后,剩下的就可以递推出来了.因为假如目前的第一个数是x,那当把他放到最后面的时候,少的逆序数是本来后面比他小的数的个数.多的逆序数就是放到后面后前面比他大的数的个数.因为所有数都是从0到n-1.所以比他小的数就是x,比他大的数就是n-

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对) ACM 题目地址:HDU 1394 Minimum Inversion Number 题意: 给一个序列由[1,N]构成,可以通过旋转把第一个移动到最后一个. 问旋转后最小的逆序数对. 分析: 注意,序列是由[1,N]构成的,我们模拟下旋转,总的逆序数对会有规律的变化. 求出初始的逆序数对再循环一遍就行了. 至于求逆序数对,我以前用归并排序解过这道题:点这里. 不过由于数据范围是5000,所以完全可以用线

HDU 1394 Minimum Inversion Number(线段树求逆序对)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移

hdoj 1394 Minimum Inversion Number【线段树求逆序对】

求逆序对有很多算法,这里说一下线段树求逆序对的思想. 知识点:线段树,逆序对,单点更新,成段求和 算法:线段树求逆序数的前提条件是要离散化,变成连续的点,首先建树,每个节点设置一个num值为0. 然后根据逆序对的定义,前面出现过的比当前数大的个数的和,我们需要求前面的比他大的数,其实就相当于从当前a[i]点对他后面所有出现过的数求和一次.然后把当前点的值在线段树叶子节点变为1,表示出现过,并向上更新到线段树里面.比如说样例4 2 1 5 3 首先4后面没有值,4更新为1,4--5区间更新为1,1

UVA 11983 Weird Advertisement(线段树求矩形并的面积)

UVA 11983 题目大意是说给你N个矩形,让你求被覆盖k次以上的点的总个数(x,y<1e9) 首先这个题有一个转化,吧每个矩形的x2,y2+1这样就转化为了求N个矩形被覆盖k次以上的区域的面积 由于坐标很大,首先考虑的就是将坐标离散化,然后建立线段树tree[][K],表示x的某个区间被覆盖了K次(大于K次算K次)的实际长度,在计算时把矩形转化为一系列横线段(就是说将一个矩形拆开为两条线段,下面的标记为1,上面的标记为-1(这里的标记很有技巧)),然后将这些线段按照y值的从小到达排序(y值相