zoukankan      html  css  js  c++  java
  • HDU 5919 Sequence ll

    Time limit 4500 ms
    Memory limit 131072 kB
    OS Windows
    Source 2016中国大学生程序设计竞赛(长春)-重现赛

    中文题意

    一个长度为n的序列,里面的数字值不超过n(这个题面没说清楚,只说了n和(a_i)的上限都是(2×10^5))。每次询问一个区间,问这个区间里面,从左到右第一次出现的那些数字组成的新序列,的“中位数”在原序列中出现的位置。比如,假设被询问的区间张这样({1,1,2,2,3,2,5,4}),那么里面第一次出现的数字就是({1,2,3,5,4}),所求的中位数就是3(按大小排序之后)。另外,这里的“中位数”不同于数学上的,比如({2,3,4,5}),答案是3,换句话说,设第一次出现的数字个数为(len),那么所求位置是(lceilfrac{len}{2} ceil),代码里就写成(lfloorfrac{len+1}{2} floor)

    解题思路

    首先对于“第一次出现”这一要求,可以参考这里[SDOI2009]HH的项链,从后向前扫的过程中(因为要求第一个出现的,所以要从后往前,确保pos数组最终存着第一个出现的(从前到后也不是搞不出来))使用pos数组记录某数字目前第一次出现的位置,然后用一棵线段树记录区间内“第一次出现的数”的位置,比如,对于序列({1,1,2,2,2,3,3}),我们建线段树时所用的序列是({1,0,1,0,0,1,0}),在第一次出现某数字的地方加一。这个序列是关键,下面的主席树就是基于这个序列建起来的。

    对于“中位数”这一要求,我们可以把上面那个线段树可持久化,套上主席树,从后向前建树,每个位置一个版本,如果当前这个数字在之前建树时出现过,那么就在这个版本的线段树上,把上次出现的位置减1,把这个位置加一,然后更新pos数组。

    询问区间([l,r])的时候,先在(l)对应的线段树上查询该区间内有多少个第一次出现的数,我的代码里就是quesum()函数,将查询结果——“该区间内第一次出现的数”的数量赋值给k,那么我们要求的答案就是主席树中第(l)个版本的线段树上第(lfloorfrac{k+1}{2} floor)个1所在的位置(quek()函数)。(类似区间第k小,但由于这题的题意和从后向前建树的方法,求第k小的时候不用两棵树相减,求一棵树就够了)

    文笔不够,感觉有些地方还是不够清楚……我是看yyb的博客看懂的

    另外分块也能做这题……HDU 5919 分块做法(留坑不填了

    源代码

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    
    const int N=200010;
    
    int T;
    int n,m;
    int a[N],pos[N];
    
    struct Node{
    	int lson,rson;
    	int sum;
    }t[N*40];//这题要40倍才够,32倍都不够。yyb的博客上32倍,那是因为他的N是222222
    int root[N],cnt;
    /*void build(int &x,int l,int r)
    {
    	x=cnt++;
    	t[x]={0,0,0};
    	if(l==r) return;
    	int mid=l+r>>1;
    	build(t[x].lson,l,mid);
    	build(t[x].rson,mid+1,r);
    }*/
    void update(int &x,int pre,int l,int r,int pos,int delta)
    {
    	x=cnt++;
    	t[x] = t[pre];
    	t[x].sum+=delta;
    	if(l==r) return;
    	int mid=l+r>>1;
    	if(pos<=mid)
    		update(t[x].lson,t[pre].lson,l,mid,pos,delta);
    	else
    		update(t[x].rson,t[pre].rson,mid+1,r,pos,delta);
    }
    int quek(int x,int l,int r,int k)
    {
    	if(l==r) return l;
    	int delta=t[t[x].lson].sum,mid=l+r>>1;
    	if(k<=delta) return quek(t[x].lson,l,mid,k);
    	else return quek(t[x].rson,mid+1,r,k-delta);
    }
    
    int quesum(int x,int l,int r,int ql,int qr)
    {
    	if(ql>r||qr<l) return 0;
    	if(ql<=l&&r<=qr) return t[x].sum;
    	int mid=l+r>>1;
    	return quesum(t[x].lson,l,mid,ql,qr)+quesum(t[x].rson,mid+1,r,ql,qr);
    }
    
    int main()
    {
    	//freopen("test.in","r",stdin);
    	scanf("%d",&T);
    	for(int ttt=1;ttt<=T;ttt++)
    	{
    		printf("Case #%d:",ttt);
    		cnt=1;
    		memset(pos,0,sizeof(pos));
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++)
    			scanf("%d",a+i);
            //build(t[n+1],1,n);//看了yyb的博客我才知道,这题的主席树最开始为空的版本不需要建树,浪费空间。原因是这道题查询的时候是单独查一棵树,而不是查询两棵树后做差。
    		for(int i=n;i;i--)
    		{
    			if(!pos[a[i]]) update(root[i],root[i+1],1,n,i,1),pos[a[i]]=i;
    			else
    			{
    				update(root[i],root[i+1],1,n,i,1);
    				update(root[i],root[i],1,n,pos[a[i]],-1);
    				pos[a[i]]=i;
    			}
    		}
    		int ans = 0, l, r;
    		while(m--)
    		{
    			scanf("%d%d",&l,&r);
    			l=(l+ans)%n+1,r=(r+ans)%n+1;
    			if (l > r) std::swap(l, r);
    			int k=quesum(root[l],1,n,l,r);
    			k=k+1>>1;
    			printf(" %d",ans=quek(root[l],1,n,k));
    		}
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    通过HTTP发包工具了解HTTP协议
    Oracle之数据库安全
    SQL注入深入剖析
    apache中如何调用CGI脚本
    fastcgi php-cgi与php-fpm区别和之间的关系
    使用PHPExcel实现Excel文件的导入和导出(模板导出)
    学会数据库读写分离、分表分库
    框架Thinkphp5 简单的实现行为 钩子 Hook
    php文件下载
    PHP为JSON数据的API返回空数组或者空对象
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/11250811.html
Copyright © 2011-2022 走看看