zoukankan      html  css  js  c++  java
  • 并不对劲的loj3048:p5283:[十二省联考]异或粽子

    题目大意

    (n)((nleq5 imes10^5))个数(a_1,a_2,...a_n)((a_ileq 2^{32}-1))
    求区间异或和前(k(kleq2 imes10^5))大之和

    题解

    考虑二分,找出第(k)大异或和是多少
    将每个位置上的数变成前缀异或和(s_i)后,建出可持久化trie树,第(i)个版本由(0,s_1,s_2,..,s_i)组成,trie树上每个点维护这个点的子树中有几个数
    二分(x),判断是否有不超过(k)个异或和
    发现这样时间复杂度为(Theta(n imes log^2 a)),略多
    发现一般情况下在线段树上二分时都不是先二分出一个值再查询,而是在线段树上边走边计算
    同理,本题就可以维护(n)个位置,即当前走到的第(i(iin[1,n]))个版本的trie树的位置
    每次判断当前位放1会不会使大于等于当前值的异或和数量少于(k),会就走(a_i)当前位异或0的方向,反之就走(a_i)当前位异或1的方向
    算出第(k)大异或和后,直接在trie树中dfs找出大于(注意,不能找等于它的,不然会找多)它的异或和,计入答案,顺便记一共加了几个数
    ((k-上一步加的数的个数))就是等于第(k)大异或和的异或和的个数,把这个计入答案
    以上两步中走到的点只有大于第(k)大异或和的点和它们的父亲,只有(Theta(k imes logspace a))
    总时间复杂度(Theta((n+k) imes logspace a))

    代码
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<iomanip>
    #include<iostream>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<vector>
    #define rep(i,x,y) for(register int i=(x);i<=(y);++i)
    #define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
    #define view(u,k) for(int k=fir[u];~k;k=nxt[k])
    #define maxn 500003
    #define maxnd (maxn*33)
    #define LL long long
    #define int LL
    using namespace std;
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)&&ch!='-')ch=getchar();
    	if(ch=='-')f=-1,ch=getchar();
    	while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    	return x*f;
    }
    void write(LL x)
    {
    	if(x==0){putchar('0'),putchar('
    ');return;}
    	int f=0;char ch[20];
    	if(x<0)putchar('-'),x=-x;
    	while(x)ch[++f]=x%10+'0',x/=10;
    	while(f)putchar(ch[f--]);
    	putchar('
    ');
    	return;
    }
    LL ans,a[maxn],num;;
    const int len=31;
    int n,k,ch[maxnd][2],val[maxnd],rt[maxn],cntnd,now[maxn],nownum;
    void ext(int i)
    {
    	int u,nu=++cntnd;rt[i]=cntnd;
    	if(i)u=rt[i-1];
    	else u=0;
    	dwn(j,len,0)
    	{
    		int d=(a[i]&(1ll<<j))>>j;
    		if(u)val[nu]=val[u]+1,ch[nu][d^1]=ch[u][d^1],u=ch[u][d];
    		else val[nu]=1;
    		ch[nu][d]=++cntnd,nu=cntnd;
    	}
    	if(u)val[nu]=val[u]+1;
    	else val[nu]=1;
    }
    void getans(int u,LL ai,LL xi)
    {
    	if(!ch[u][0]&&!ch[u][1]){if(((LL)ai^(LL)xi)>(LL)num){nownum+=val[u],ans=ans+(LL)((LL)ai^(LL)xi)*val[u];}return;}
    	if(ch[u][0])getans(ch[u][0],ai,xi<<1);
    	if(ch[u][1])getans(ch[u][1],ai,xi<<1|1);
    	return;
    }
    #define dd(x,y) ((x&(1ll<<y))>>y)
    signed main() 
    {
    	n=read(),k=read();
    	rep(i,1,n)a[i]=read()^a[i-1];
    	rep(i,0,n)ext(i),now[i]=rt[i];
    	dwn(i,len,0)
    	{
    		int tmp=0;
    		rep(j,1,n)if(now[j]){tmp+=val[ch[now[j]][dd(a[j],i)^1ll]];}
    		if(tmp+nownum>=k){rep(j,1,n)if(now[j])now[j]=ch[now[j]][dd(a[j],i)^1ll];num=num<<(1ll)|(1ll);}
    		if(tmp+nownum<k){rep(j,1,n)if(now[j])now[j]=ch[now[j]][dd(a[j],i)];nownum+=tmp;num=num<<(1ll);}
    	}
    	nownum=0;
    	rep(i,1,n)
    	{
    		int u=rt[i],vv=0;
    		dwn(j,len,0)
    		{
    			int d=dd(a[i],j),e=dd(num,j);
    			if(!e&&ch[u][d^1]){getans(ch[u][d^1],a[i],vv<<1|(d^1));}
    			if(!ch[u][d^e])break;
    			u=ch[u][d^e],vv=vv<<1|(d^e);
    		}
    	}
    	ans+=(LL)num*(k-nownum);
    	write(ans);
    	return 0;
    }
    /*
    3 2
    1 2 3
    */
    
    
    一些感想

    sb猎人公会nmsl

  • 相关阅读:
    设计模式之美学习-设计原则-面向对象基本概念(一)
    redis-分布式锁-设计与使用
    linux常用命令记录(一)
    redis-布隆过滤器使用
    jdk源码阅读-ConcurrentLinkedQueue(一)
    支付宝支付接口-app支付沙箱环境
    RocketMQ-安装
    支付宝支付接口-运行支付宝demo
    elasticsearch-文档-父子文档(十一)
    RTMPdump 源代码分析 1: main()函数
  • 原文地址:https://www.cnblogs.com/xzyf/p/10679993.html
Copyright © 2011-2022 走看看