• 【31.94%】【BZOJ 3489】A simple rmq problem


    Time Limit: 40 Sec  Memory Limit: 600 MB
    Submit: 1434  Solved: 458
    [Submit][Status][Discuss]

    Description

    因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。

     

    Input

    第一行为两个整数N,MM是询问数,N是序列的长度(N<=100000M<=200000)

    第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N

    再下面M行,每行两个整数xy

    询问区间[l,r]由下列规则产生(OIER都知道是怎样的吧>_<)

    l=min(x+lastans)mod n+1,(y+lastansmod n+1);

    r=max(x+lastans)mod n+1,(y+lastansmod n+1);

    Lastans表示上一个询问的答案,一开始lastans0

    Output

    一共M行,每行给出每个询问的答案。

    Sample Input

    10 10
    6 4 9 10 9 10 9 4 10 4
    3 8
    10 1
    3 4
    9 4
    8 1
    7 8
    2 9
    1 1
    7 3
    9 9

    Sample Output

    4
    10
    10
    0
    0
    10
    0
    4
    0
    4

    HINT



    注意出题人为了方便,input的第二行最后多了个空格。


    2015.6.24新加数据一组,2016.7.9放至40S,600M,但未重测


     

    Source


    【题解】

    这题如果用code forces中44G的遍历kd-tree方法来做。会超时。

    说下思路。

    设[l,r]内不重复的数字的下标为x;

    则设pre[x],last[x]为x上一次出现的下标位置,和下一次出现的下标位置。

    如果之前没有出现过则为0,如果后面没出现过则为n+1;

    则pre[x]<l 且 last[x] >r,且x∈[l,r]

    则以(x,pre[x],last[x])作为一个3维的点。

    我们如果输入为(l,r)。则要找到一个点 l <= x <= r,pre[x] <l,last[x] > r;

    即寻找某个范围内的点。然后要求key值是最大的而已。

    【代码】

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int MAXN = 109000;
    
    struct point
    {
    	int d[3], l, r, max, n,ma_x[3],mi_n[3];
    };
    
    int n, m, pre[MAXN] = { 0 }, last[MAXN], root, now, mi_n, ma_x,ans = 0;
    point t[MAXN];
    
    void input_data()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; i++)
    	{
    		scanf("%d", &t[i].n);
    		t[i].d[0] = i;
    		t[i].d[1] = pre[t[i].n];
    		pre[t[i].n] = i;
    	}
    	for (int i = n; i >= 1; i--)
    		last[t[i].n] = n + 1;
    	for (int i = n; i >= 1; i--)
    	{
    		int temp = t[i].n;
    		t[i].d[2] = last[temp];
    		last[temp] = i;
    	}
    }
    
    bool cmp(point a, point b)
    {
    	return a.d[now] < b.d[now];
    }
    
    void up_data(int rt)
    {
    	int l = t[rt].l, r = t[rt].r;
    	if (l)
    	{
    		for (int i = 0; i <= 2; i++)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[l].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[l].mi_n[i]);
    		}
    		t[rt].max = max(t[rt].max, t[l].max); 
    	}
    	if (r)
    	{
    		for (int i = 0; i <= 2; i++)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[r].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[r].mi_n[i]);
    		}
    		t[rt].max = max(t[rt].max, t[r].max);
    	}
    }
    
    int build(int begin, int end, int fx)
    {
    	int m = (begin + end) >> 1;
    	now = fx;
    	nth_element(t + begin, t + m, t + end + 1, cmp);
    	t[m].max = t[m].n;//max最后保存的是这棵子树的key值的最大值。
    	for (int i = 0; i <= 2; i++)//这是这棵树中最小最大的3维坐标。
    		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
    	if (begin < m)
    		t[m].l = build(begin, m - 1, (fx + 1) % 3);
    	if (m < end)
    		t[m].r = build(m + 1, end, (fx + 1) % 3);
    	up_data(m);
    	return m;
    }
    
    void get_ans()
    {
    	root = build(1, n, 0);
    }
    
    bool ok(int rt) //判断这个子树有没有可能有解
    {
    	if (t[rt].ma_x[0] < mi_n || t[rt].mi_n[0] > ma_x)
    		return 0;
    	if (t[rt].mi_n[1] >= mi_n || t[rt].ma_x[2] <= ma_x)
    		return 0;
    	return 1;
    }
    
    void query(int rt)
    {
    	if (rt == 0)
    		return;
    	if (mi_n <= t[rt].mi_n[0] && t[rt].ma_x[0] <= ma_x && t[rt].ma_x[1] < mi_n
    		&& t[rt].mi_n[2] > ma_x) //这是整个子树都可以作为解的情况
    	{
    		ans = max(ans, t[rt].max);
    		return;
    	}
    	if (mi_n <= t[rt].d[0] && t[rt].d[0] <= ma_x && t[rt].d[1] < mi_n
    		&& t[rt].d[2] > ma_x)//这个则是单个节点。
    		ans = max(ans, t[rt].n);
    	int l = t[rt].l, r = t[rt].r;
    	if (t[l].max > t[r].max)//往下走。
    	{
    		if (t[l].max > ans && ok(l))
    			query(l);
    		if (t[r].max > ans && ok(r))
    			query(r);
    	}
    	else
    	{
    		if (t[r].max > ans && ok(r))
    			query(r);
    		if (t[l].max > ans && ok(l))
    			query(l);
    	}
    }
    
    void output_ans()
    {
    	int la = 0;
    	for (int i = 1; i <= m; i++)
    	{
    		int x, y;
    		scanf("%d%d", &x, &y);
    		mi_n = min(((x + la) % n) + 1, ((y + la) % n) + 1);
    		ma_x = max(((x + la) % n) + 1, ((y + la) % n) + 1);
    		ans = 0;
    		query(root);
    		printf("%d
    ", ans);
    		la = ans;
    	}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;
    }



  • 相关阅读:
    【Java】XML文件的解析
    PE知识复习之PE合并节
    PE知识复习之PE的重定位表
    PE知识复习之PE的两种状态
    PE知识复习之PE的节表
    PE知识复习之PE的各种头属性解析
    PE知识复习之PE的导入表
    PE知识复习之PE的导出表
    PE知识复习之PE的绑定导入表
    第三讲扩展,VA,RVA,FA(RAW),模块地址的概念
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632239.html
Copyright © 2020-2023  润新知