hdu3333 线段树+离散化+离线处理

可以先做做3874    哪道题数据小 不用离散化

题意是让询问区间和    出现过多次的只能算一次  很明显的线段树

先对询问区间按右值排序(也可以按左值  其实都一样)并记录输入的顺序(用来按输入的顺序输出)  然后进行跟新和查询   如下

对每一个要跟新的值先判断之前出没出现过   如果出现过就消除之前的     再跟新当前的  并记录位置(此处就是为什么要用到离散化)  如果当前跟新的和查询的右值相同则查询

这样做的原因是消除之前出现过的是不会对当前造成影响(想想为什么)  还有就是跟新比当前右值大的位置就没有意义 具体看代码     还有就是注意__int64

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;

#define LL(x) (x<<1)
#define RR(x) ((x<<1)|1)

struct node
{
    int L,R,ii;
}A[100010];
struct Node
{
    int i;
    int tt;
}B[30010];
int map[30010];
int cmp(node a,node b)
{
    return a.R<b.R;
}
int cmp1(Node a,Node b)
{
    return a.tt<b.tt;
}
int cmp2(Node a,Node b)
{
    return a.i<b.i;
}
__int64 num[4*30010];
int update(int L,int R,int pos,int mark,int k)
{
    if(k>0)num[mark]+=map[k];
    else num[mark]-=map[-k];
    if(L==R&&L==pos)
    {
        return 0;
    }
    int mid=(L+R)/2;
    if(pos<=mid)
    {
        update(L,mid,pos,LL(mark),k);
    }
    else update(mid+1,R,pos,RR(mark),k);
    return 0;
}
__int64 find(int L,int R,int left,int right,int mark)
{
    if(L==left&&R==right)
    {
        return num[mark];
    }
    int mid=(L+R)/2;
    if(right<=mid)
    {
        return find(L,mid,left,right,LL(mark));
    }
    else if(left>mid)
    {
        return find(mid+1,R,left,right,RR(mark));
    }
    else return find(L,mid,left,mid,LL(mark))+find(mid+1,R,mid+1,right,RR(mark));
}
int main()
{
    int T,i,j,n,m,a,b;
    int leap[30010];
    __int64 print[100010];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&B[i].tt);
            B[i].i=i;
        }
        scanf("%d",&m);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&A[i].L,&A[i].R);
            A[i].ii=i;
        }
        memset(num,0,sizeof(num));
        sort(B+1,B+1+n,cmp1);
        sort(A+1,A+1+m,cmp);
        int k=-1;
        j=0;
        for(i=1;i<=n;i++)
        {
            if(B[i].tt!=k)
            {
                k=B[i].tt;
                j++;
                map[j]=k;
                B[i].tt=j;
            }
            else B[i].tt=j;
        }
        sort(B+1,B+1+n,cmp2);
        memset(leap,0,sizeof(leap));
        for(i=1,j=1;i<=n&&j<=m;i++)
        {
            if(leap[B[i].tt]) update(1,n,leap[B[i].tt],1,-B[i].tt);
            update(1,n,i,1,B[i].tt);
            leap[B[i].tt]=i;
            while(i==A[j].R)
            {
                print[A[j].ii]=find(1,n,A[j].L,A[j].R,1);
                j++;
                if(j>m) break;
            }
        }
        for(i=1;i<=m;i++)
        printf("%I64d\n",print[i]);
    }
    return 0;
}
时间: 2024-11-06 09:42:49

hdu3333 线段树+离散化+离线处理的相关文章

Partition(线段树的离线处理)

有一点类似区间K值的求法. 这里有两颗树,一个是自己建的线段树,一个是题目中给定的树.以线段树和树进行区分. 首先离散化一下,以离散化后的结果建线段树,线段树的节点开了2维,一维保存当前以当前节点为权值的树的节点是往左走的,另一维是往右走的,用一个vector保存一下以当前i节点为结束的询问,因为所有的询问都是从根节点出发的,只需保存终点即可. 然后从根节点出发遍历整棵树,当走到当前节点时,枚举以当前节点的询问q[i],然后求出比q[i].x大的向左和向右走的节点个数,以及比它小的个数,如果有相

POJ_2528 Mayor&#39;s poster(线段树+离散化)

题目请点我 题解: 这道题与之前的题目相比重点在于一个映射的预处理,题目所给的区间达到10000000,而最多只有10000个点,如果直接建树的话太过于空旷.把这些区间的左右节点一一对应,最多有4×10000个点,远小于之前的10000000,而且区间之间的对应关系也不会改变. 举个例子: 区间:[2,6],[4,8],[6,10] 我们进行下面对应: 2 4 6 8 10 1 2 3 4 5 则原区间变为[1,3],[2,4],[3,5].可以发现它们之间的覆盖关系并没有改变,但是却紧凑了很多

POJ - 2528 - Mayor&#39;s posters 【线段树+离散化+补点】

http://poj.org/problem?id=2528 #include <cstdio> #include <iostream> #include <set> #include <cstring> #include <string> #define left rt<<1 #define right rt<<1|1 using namespace std; const int MAXN = 32768 + 5; in

HDU5124:lines(线段树+离散化)或(离散化思想)

http://acm.hdu.edu.cn/showproblem.php?pid=5124 Problem Description John has several lines. The lines are covered on the X axis. Let A is a point which is covered by the most lines. John wants to know how many lines cover A. Input The first line conta

hdu1828 Picture(线段树+离散化+扫描线)两种方法

C - Picture Time Limit:2000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Description A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or

Poj 2528 Mayor&#39;s posters (线段树+离散化)

题目连接: http://poj.org/problem?id=2528 题目大意: 有10000000块瓷砖,n张海报需要贴在墙上,每张海报所占的宽度和瓷砖宽度一样,长度是瓷砖长度的整数倍,问按照所给海报顺序向瓷砖上贴海报,最后有几张海报是可见的? 解题思路: 因为瓷砖块数和海报张数多,首选线段树,如果按照常规的建树方式,把瓷砖当做数的节点,肯定会MTL......... 所以我们可以用海报的起点和终点当做树的节点,这样树的节点才有20000个,但是这样建树的话,求海报覆盖了那些节点会很复杂,

hdu 5124 lines (线段树+离散化)

lines Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 620    Accepted Submission(s): 288 Problem Description John has several lines. The lines are covered on the X axis. Let A is a point which

poj2528 线段树+离散化

1 //Accepted 1960K 110MS 2 //线段树+离散化 3 //把所有的坐标排序,从小到大编号,建立线段树 4 #include <cstdio> 5 #include <cstring> 6 #include <iostream> 7 #include <queue> 8 #include <cmath> 9 #include <algorithm> 10 using namespace std; 11 /** 1

POJ 2528 (线段树+离散化) Mayor&#39;s posters

因为将每个单位都作为一个最小单元的话会爆内存的 所以,将海报的每个端点进行排序,将这些端点最为最小的区间. 毕竟是刚刚接触线段树,理解起来还有些吃力,还是那句话,题做多了慢慢就好了. 萌萌的AC代码君贴上. 1 //#define LOCAL 2 #include <iostream> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 int n; 8 struct CPost 9