• luogu4462 异或序列


    题目大意

    给出n,m,k,有n个数的序列,m次询问一段区间,问异或和等于K的子区间的个数。

    题解

    本题一看就是莫队。但要解决该题需要以下性质:

    定理:

    $$aoplus b=cLeftrightarrow aoplus c=bLeftrightarrow boplus c=a$$

    推论:

    $$oplus_{i=l}^r A_i= oplus_{i=1}^r A_i oplus oplus_{i-1}^{l-1}A_i$$

    因此,我们对每个节点维护它的前缀和。比如说,如果右方加入一个节点,因为右方r的前缀和异或左面l的前缀和的结果表示的就是[l+1,r]的异或和。此时增加的满足条件的子区间的个数,根据定理,便是当前区间中前缀和的值异或上新加入的节点的前缀和的值等于K的点的数量。这就可以用莫队的桶来维护了。

    #define _DEBUG
    
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    
    
    const int MAX_N = 100010;
    int K;
    int BASE;
    
    struct CaptainMo
    {
    	int N, OpCnt, Sum;
    	
    	int PrefixValCnt[MAX_N];
    	
    	struct Data
    	{
    		int CurVal, Prefix;
    	}_datas[MAX_N];
    	
    	struct Query
    	{
    		int L, R;
    		int Ans;
    		Query *This;
    		
    		Query():This(this){}
    		
    		bool operator < (const Query& a)const
    		{
    			return L / BASE == a.L / BASE ? R < a.R : L / BASE < a.L / BASE;
    		}
    	}_qs[MAX_N], temp[MAX_N];
    	
    	void InRange(Data cur)
    	{
    		Sum += PrefixValCnt[cur.Prefix ^ K];
    		PrefixValCnt[cur.Prefix]++;
    	}
    	
    	void OutRange(Data cur)
    	{
    		Sum -= PrefixValCnt[cur.Prefix ^ K];
    		PrefixValCnt[cur.Prefix]--;
    	}
    	
    	void Init()
    	{
    		for(int i = 1; i <= OpCnt; i++)
    			_qs[i].L--;
    		for(int i = 1; i <= N; i++)
    			_datas[i].Prefix = _datas[i - 1].Prefix ^ _datas[i].CurVal;
    		BASE = sqrt(N);
    		memcpy(temp, _qs, sizeof(_qs));
    		sort(temp + 1, temp + OpCnt + 1);
    	}
    	
    	void Proceed()
    	{
    		int l = 0, r = 0;
    		Sum = 0;
    		PrefixValCnt[0] = 1;
    		for(int i = 1; i <= OpCnt; i++)
    		{
    			while(r > temp[i].R)
    				OutRange(_datas[r--]);
    			while(r < temp[i].R)
    				InRange(_datas[++r]);
    			while(l < temp[i].L)
    				OutRange(_datas[l++]);
    			while(l > temp[i].L)
    				InRange(_datas[--l]);
    			temp[i].This->Ans = Sum;
    		}
    	}
    }g;
    
    int main()
    {
    	scanf("%d%d%d", &g.N, &g.OpCnt, &K);
    	for(int i = 1; i <= g.N; i++)
    		scanf("%d", &g._datas[i].CurVal);
    	for(int i = 1; i <= g.OpCnt; i++)
    		scanf("%d%d", &g._qs[i].L, &g._qs[i].R);
    	g.Init();
    	g.Proceed();
    	for(int i = 1; i <= g.OpCnt; i++)
    		printf("%d
    ", g._qs[i].Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    跟我学SharePoint 2013视频培训课程——什么是SharePoint 2013(1)
    SharePoint 关于拓扑错误的解决方案
    SharePoint 2010、2013多个域之间互信(Domain Trust)的设计与实施
    SharePoint 2013 Disaster Recovery——迁移内容数据库
    windows-根据进程PID 获取进程路径
    kernel 获取ntoskrnl.exe基址
    ring3 x32挂起进程注入原理.
    CryEntryBuffer
    windows内核代码之进程操作
    驱动中遍历模块,以及获取ntoskrnl.exe基址
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9219452.html
Copyright © 2020-2023  润新知