莫队算法专题

20 篇文章 0 订阅
9 篇文章 0 订阅

BZOJ【2038】—— [2009国家集训队]小Z的袜子(hose)

Time Limit: 20 SecMemory Limit: 259 MB
Description

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

Input

输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。

Output

包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

Sample Input

6 4

1 2 3 3 3 2

2 6

1 3

3 5

1 6

Sample Output

2/5

0/1

1/1

4/15

【样例解释】

询问1:共C(5,2)=10种可能,其中抽出两个2有1种可能,抽出两个3有3种可能,概率为(1+3)/10=4/10=2/5。

询问2:共C(3,2)=3种可能,无法抽到颜色相同的袜子,概率为0/3=0/1。

询问3:共C(3,2)=3种可能,均为抽出两个3,概率为3/3=1/1。

注:上述C(a, b)表示组合数,组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。

【数据规模和约定】

30%的数据中 N,M ≤ 5000;

60%的数据中 N,M ≤ 25000;

100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

Source

版权所有者:莫涛

莫队算法的裸题,我们可以在O(1)的时间复杂度里面算出对于已经知道结果的区间向外或者向内对结果的改变,那么我们就可以离线处理这些问题,将区间进行分块处理,(对时间复杂度感兴趣的小伙伴可以自行搜索)。那么区间的总的方案数为 (RL+1)×(RL)2 。那么概率我们就可以表示出来

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

typedef long long LL;

const int PL = 500;

typedef struct node
{
    int L,R;

    int Id ;

    node(){}

    node(int _L,int _R):L(_L),R(_R){}

    bool operator < (const node &a)const\\区间分块
    {
        return (L/PL == a.L/PL) ? (R<a.R):(L/PL<a.L/PL);
    }
}Section;

Section s[55000];

int a[55000];

LL ansm[55000];

LL ansd[55000];

int num[55000];

int main()
{
    int n,m;

    while(~scanf("%d %d",&n,&m))
    {
        for(int i = 1;i<=n;i++) scanf("%d",&a[i]);

        for(int i = 1;i<=m;i++) scanf("%d %d",&s[i].L,&s[i].R),s[i].Id = i;

        sort(s+1,s+m+1);

        LL sum = 0;

        int l = 0,r = 1;

        memset(num,0,sizeof(num));

        for(int i = 1;i<=m;i++)
        {
            while(r<=s[i].R)
            {
                sum+=(num[a[r]]);

                num[a[r]] ++;

                r++;
            }

            while(r>s[i].R+1)
            {
                r--;

                num[a[r]] --;

                sum-=num[a[r]];

            }

            while(l<s[i].L-1)
            {
                l++;

                num[a[l]]--;

                sum-=num[a[l]];

            }

            while(l>=s[i].L)
            {
                sum+=num[a[l]];

                num[a[l]]++;

                l--;
            }

            ansm[s[i].Id] = sum;

            ansd[s[i].Id] = (1LL*(s[i].R-s[i].L+1)*(s[i].R-s[i].L))/2;
        }

        for(int i =1;i<=m;i++)
        {
            if(ansm[i] ==0) printf("0/1\n");
            else
            {
                LL g = __gcd(ansm[i],ansd[i]);

                printf("%lld/%lld\n",ansm[i]/g,ansd[i]/g);
            }
        }
    }
    return 0;
}

Codeforces 86D. Powerful array


time limit per test5 secondsmemory limit per test256 megabytes

An array of positive integers a1,a2,...,an is given. Let us consider its arbitrary subarray al,al+1...,ar , where 1 ≤ l ≤ r ≤ n. For every positive integer s denote by Ks the number of occurrences of s into the subarray. We call the power of the subarray the sum of products Ks·Ks·s for every positive integer s. The sum contains only finite number of nonzero summands as the number of different values in the array is indeed finite.

You should calculate the power of t given subarrays.

Input

First line contains two integers n and t (1 ≤ n, t ≤ 200000) — the array length and the number of queries correspondingly.

Second line contains n positive integers ai (1 ≤ ai ≤ 106) — the elements of the array.

Next t lines contain two positive integers l, r (1 ≤ l ≤ r ≤ n) each — the indices of the left and the right ends of the corresponding subarray.

Output

Output t lines, the i-th line of the output should contain single positive integer — the power of the i-th query subarray.

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preferred to use cout stream (also you may use %I64d).

Examples

Input

3 2
1 2 1
1 2
1 3

Output

3
6

Input

8 3
1 1 2 2 1 3 1 1
2 7
1 6
2 7

Output

20
20
20

Note

Consider the following array (see the second sample) and its [2, 7] subarray (elements of the subarray are colored):
Then K1 = 3, K2 = 2, K3 = 1, so the power is equal to 32·1 + 22·2 + 12·3 = 20.

和上面的题基本上是一样的,只不过是在更行的时候有些不同罢了,假设现在值为s的数的个数为a,我们要增加他的数量,那么我们的更新为 sum=sums×a2+s×(a+1)2=sum+s×(2a+1) ,减少数量的时候为 sum=sums×a2+s×(a1)2=sums×(2a1) 。同时分块的时候发现块的大小为500超时,1000左右比价的快。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int BO = 1000;

const int Max = 210000;

const int MaxM = 1e6+100;

typedef struct node
{
    int L,R,Id;

    bool operator < (const node &a)const
    {
        return (L/BO == a.L/BO)?(R<a.R) : (L/BO<a.L/BO);
    }
}Section;

Section s[Max];

int a[Max];

LL num[MaxM];

LL ans[Max];

int main()
{
    int n,m;

    scanf("%d %d",&n,&m);

    for(int i = 1;i<=n;i++) scanf("%d",&a[i]);

    for(int i = 1;i<=m;i++) scanf("%d %d",&s[i].L,&s[i].R),s[i].Id = i ;

    sort(s+1,s+m+1);

    memset(num,0,sizeof(num));

    LL sum = 0;

    int l  = 0,r=  1;

    for(int i = 1;i<=m;i++)
    {
        while(r<=s[i].R)
        {
            sum+=((num[a[r]]*2+1)*a[r]);

            num[a[r]] ++;

            r++;
        }
        while(r>s[i].R+1)
        {
            r--;

            sum-=((num[a[r]]*2-1)*a[r]);

            num[a[r]]-- ;
        }

        while(l<s[i].L-1)
        {
            l++;

            sum-=((num[a[l]]*2-1)*a[l]);

            num[a[l]]-- ;
        }


        while(l>=s[i].L)
        {
            sum+=((num[a[l]]*2+1)*a[l]);

            num[a[l]]++;

            l--;
        }

        ans[s[i].Id] = sum;
    }

    for(int i = 1;i<=m;i++)
    {
        printf("%I64d\n",ans[i]);
    }
    return 0;
}

Bzoj【 3289】—— Mato的文件管理


Time Limit: 40 SecMemory Limit: 128 MB
Description

Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号。为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问。Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料。Mato有一个习惯,他总是从文件大小从小到大看资料。他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能随机访问)。Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?

Input

第一行一个正整数n,表示Mato的资料份数。
第二行由空格隔开的n个正整数,第i个表示编号为i的资料的大小。
第三行一个正整数q,表示Mato会看几天资料。
之后q行每行两个正整数l、r,表示Mato这天看[l,r]区间的文件。

Output

q行,每行一个正整数,表示Mato这天需要交换的次数。

Sample Input

4

1 4 2 3

2

1 2

2 4

Sample Output

0

2

HINT

Hint

n,q <= 50000

样例解释:第一天,Mato不需要交换

第二天,Mato可以把2号交换2次移到最后。

Source

By taorunz

莫队的裸题,区间文件的交换的次数就是这个区间的逆序对的个数,那么对于一个数子来数,怎么样比较快的计算出逆序对呢?用树状数组。这个题比较坑的就是没有说明文件大小的范围,所有要先离散化一下。然后就是莫队的处理。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <algorithm>

using namespace std;

typedef long long LL;

const int Bo = 400;

const int Max = 55000;

const int MaxN = 50000;

typedef struct node
{
    int L,R,Id;

    bool operator < (const node &a)const
    {
        return (L/Bo == a.L/Bo)?(R<a.R):(L/Bo<a.L/Bo);
    }
}Section;

Section S[Max];

int Tr[Max];

int a[Max];

int b[Max];

LL ans[Max];

int n,m;

int Lowbite(int x) {return x&(-x);}

void add(int x,int va)
{
    while(x<=MaxN)
    {
        Tr[x]+=va;

        x+=Lowbite(x);
    }
}

int  Qu(int x)
{
    int ans = 0;

    while(x>0)
    {
        ans+=Tr[x];

        x-=Lowbite(x);
    }

    return ans;

}

int Search(int L,int R,int s)
{
    while(L<=R)
    {
        int mid = (L+R)>>1;

        if(b[mid] == s) return mid;

        if(b[mid]<s) L = mid+1;

        else R = mid-1;
    }

    return 0;
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i = 1;i<=n;i++) scanf("%d",&a[i]),b[i] = a[i];

        sort(b+1,b+n+1);

        b[0] = 1;

        for(int i  = 1;i<=n;i++)
        {
            if(b[b[0]]!= b[i]) b[++b[0]] = b[i];
        }

        for(int i = 1;i<=n;i++)//离散化
        {
            a[i] = Search(1,b[0],a[i]);
        }

        scanf("%d",&m);

        for(int i  =1;i<=m;i++) scanf("%d %d",&S[i].L,&S[i].R),S[i].Id = i;

        sort(S+1,S+m+1);

        memset(Tr,0,sizeof(Tr));

        LL sum = 0;

        int l = 0 ,r =  1;

        for(int i  = 1;i<=m;i++)
        {
            while(r<=S[i].R)
            {
                sum+=Qu(MaxN)-Qu(a[r]);

                add(a[r],1);

                r++;
            }

            while(r>S[i].R+1)
            {
                r--;

                sum-=(Qu(MaxN)-Qu(a[r]));

                add(a[r],-1);
            }

            while(l<S[i].L-1)
            {
                l++;

                add(a[l],-1);

                sum-=(Qu(a[l]-1));

            }

            while(l>=S[i].L)
            {
                sum+=(Qu(a[l]-1));

                add(a[l],1);

                l--;
            }

            ans[S[i].Id] = sum;
        }


        for(int i  =1;i<=m;i++)
        {
            printf("%lld\n",ans[i]);
        }

    }
    return 0;
}

HDU【5381】——The sum of gcd


|Time Limit: 2000/1000 MS (Java/Others) | Memory Limit: 65536/65536 K (Java/Others)|

Problem Description

You have an array A,the length of A is n
Let  f(l,r)=i=lr j=irgcd(ai,ai+1,aj)

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
First line has one integers n
Second line has n integers Ai
Third line has one integers Q,the number of questions
Next there are Q lines,each line has two integers l,r
1≤T≤3
1≤n,Q≤10 4
1≤ai≤10 9
1≤l < r≤n

Output

For each question,you need to print f(l,r)

Sample Input

2
5
1 2 3 4 5
3
1 3
2 3
1 4
4
4 2 6 9
3
1 3
2 4
2 3

Sample Output

9
6
16
18
23
10

Author

SXYZ

题意:询问区间中任意子区间的gcd之和。经常做区间gcd的会知道一个常识,当区间固定一个端点向某一侧延伸的时候,区间gcd是单调非增的。而且不同gcd的个数为logn个,那么我们可以提前处理出来以某个端点为起点向一个方向延伸的区间的gcd的最远的延伸的方向和对应的gcd(RMQ+二分)。处理出这些东西,剩下的区间查询就是莫队的时间复杂度再乘以单次修改的时间复杂度(logn),不可以在线查询会增加个log,会超时orz.

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int Bo = 200;

const int Max = 1e4+100;

typedef struct node
{
    int L,R,Id;

    bool operator < (const node &a)const
    {
        return (L/Bo == a.L/Bo)?(R<a.R):(L/Bo<a.L/Bo);
    }
}Section;

Section S[Max];

int n,m;

int rmq[Max][15];

int a[Max];

LL ans[Max];


vector<pair<int,int> >VL[Max];

vector<pair<int,int> >VR[Max];

int GCD(int a,int b)
{
    return b == 0 ?a:GCD(b,a%b);
}
//用RMQ查询区间gcd,保证O(1)的查询
void GetRmq()
{
    for(int i = 1;i<=n;i++)
        rmq[i][0] = a[i];

    for(int  i = 1;(1<<i)<=n;i++)
    {
        for(int j = 1;j<=n;j++)
        {
            if(j+(1<<i)-1<=n)
            {
                rmq[j][i] =GCD(rmq[j][i-1],rmq[j+(1<<(i-1))][i-1]);
            }
        }
    }

}

int Qu(int l,int r)
{
    int k = (int)log2(r-l+1);

    return GCD(rmq[l][k],rmq[r-(1<<k)+1][k]);
}
//固定s为右端点,向左延伸gcd为t的最左端的位置
int RSearch(int s,int L,int R,int t)
{
    int ans ;

    while(L<=R)
    {
        int mid = (L+R)>>1;

        if(Qu(mid,s) == t)
        {
            ans = mid;
            R = mid-1;
        }
        else L = mid+1;
    }
    return ans;
}
//固定s为左端点,向右延伸gcd为t的最右端的位置
int LSearch(int s,int L,int R,int t)
{
    int ans ;

    while(L<=R)
    {
        int mid = (L+R)>>1;

        if(Qu(s,mid) == t)
        {
            ans = mid;
            L = mid+1;
        }
        else R= mid-1;
    }
    return ans;
}

LL Rcal(int s,int t)
{
    LL ans = 0;

    int ss = s;

    for(int i = 0;i<VR[s].size();i++)
    {

        ans +=(1LL*(ss-max(t,VR[s][i].second)+1)*VR[s][i].first);

        ss = VR[s][i].second-1;

        if(ss<t) break;
    }

    return ans;
}

LL Lcal(int s,int t)
{
    LL ans = 0;

    int ss = s;

    for(int i = 0;i<VL[s].size();i++)
    {

        ans+=(1LL*(min(t,VL[s][i].second)-ss+1)*VL[s][i].first);

        ss = VL[s][i].second+1;

        if(ss>t) break;
    }

    return ans;
}

int main()
{
    int T;

    freopen("in.in","r",stdin); 

    scanf("%d",&T);

    while(T--)
    {
        scanf("%d",&n);

        for(int i = 1;i<=n;i++) scanf("%d",&a[i]);

        GetRmq();

        //处理出左端固定的不同gcd的区间段
        for(int i = 1;i<=n;i++)
        {
            int r = i;

            VL[i].clear();

            while(r<=n)
            {
                int ant = Qu(i,r);

                r = LSearch(i,r,n,ant);

                VL[i].push_back(make_pair(ant,r));

                r++;
            }
        }

        for(int i =n;i>=1;i--)
        {
            int l = i;

            VR[i].clear();

            while(l>=1)
            {
                int ant = Qu(l,i);

                l = RSearch(i,1,l,ant);

                VR[i].push_back(make_pair(ant,l));

                l--;
            }
        }

        scanf("%d",&m);

        for(int i = 1;i<=m;i++) scanf("%d %d",&S[i].L,&S[i].R) , S[i].Id = i;

        sort(S+1,S+m+1);

        LL sum = 0;

        int l = 0,r = 1;

        for(int i = 1;i<=m;i++)
        {

            while(r<=S[i].R) 
            {
                sum+=Rcal(r,l+1);

                r++;
            }

            while(r>S[i].R +1)
            {

                r --;

                sum-=Rcal(r,l+1);
            }

            while(l<S[i].L-1)
            {
                l++;

                sum-=Lcal(l,r-1);
            }

            while(l>=S[i].L)
            {
                sum+=Lcal(l,r-1);

                l--;
            }

            ans[S[i].Id] = sum;
        }

        for(int i = 1;i<=m;i++) printf("%I64d\n",ans[i]);
  }
    return 0;
}

Bzoj【4540】—— [Hnoi2016]序列


Time Limit: 20 SecMemory Limit: 512 MB
Description

  给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

Input

  输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。

Output

对于每次询问,输出一行,代表询问的答案。

Sample Input

5 5

5 2 4 1 3

1 5

1 3

2 4

3 5

2 5

Sample Output

28

17

11

11

17

HINT

1 ≤N,Q ≤ 100000,|Ai| ≤ 109

题意:对于每一个点我们可以处理出来以这个点为起点的向左的为最小值得左边界和向右为最小值的右边界,同时能够处理出来下一个最小值和对应的位置(单调栈),这样向左向右我们都可以形成一个树,我们处理出每个节点到叶子的和。对于一个区间,我们查出最小值的下标(RMQ),然后以最小为界限向外的区间最小值固定,而里面的我们可以用树的前缀和O(1)的处理出来,那么总的更行的时间复杂度为O(1)。然后就是莫队的内容

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <list>
#include <algorithm>
#define rr() freopen("in.in","r",stdin)
#define ww() freopne("out.out","w",stdout)

using namespace std;

typedef long long LL;

const int Max = 110000;

int a[Max],rmq[Max][21];

int n,m,Bo;

LL sumL[Max],sumR[Max];

LL ans[Max];

int L[Max],R[Max];

stack<int>St;

typedef struct node
{
    int L,R,Id;

    bool operator < (const node &a)const
    {
        return (L/Bo == a.L/Bo)?R<a.R:(L/Bo<a.L/Bo);
    }
}Section;

Section S[Max];
//RMQ处理区间最小值得查询,返回的为最小值的下标
void GetRmq()
{
    for(int i = 1;i<=n;i++) rmq[i][0] = i;

    for(int i  =1;(1<<i)<=n;i++)
        for(int j =1;j+(1<<i)-1<=n;j++)
            rmq[j][i] = a[rmq[j][i-1]]<a[rmq[j+(1<<(i-1))][i-1]]?rmq[j][i-1]:rmq[j+(1<<(i-1))][i-1];
}

int Qu(int L,int R)
{
    int k =(int)log2(R-L+1);

    return a[rmq[L][k]]<a[rmq[R-(1<<k)+1][k]] ? rmq[L][k]:rmq[R-(1<<k)+1][k];
}
//右边界的更新
LL Rcal(int l,int r)
{
    int s = Qu(l+1,r);

    return (1LL*(s-l)*a[s]+sumR[r]-sumR[s]);
}
//左边界的更新
LL Lcal(int l,int r)
{
    int s = Qu(l,r-1);

    return (1LL*(r-s)*a[s]+sumL[l]-sumL[s]);
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        for(int i = 1;i<=n;i++) scanf("%d",&a[i]);

        GetRmq(); Bo = (int)sqrt(n);

        for(int i = 1;i <= m; i++) scanf("%d %d",&S[i].L,&S[i].R),S[i].Id = i;

        sort(S+1,S+m+1);

        St.push(0);
        //单调栈处理出以每个点的为右边,区间最小值为对应点对值得最左边界
        for(int i = 1;i <= n;i++)
        {
            while(St.size() > 1 && a[St.top()] > a[i]) St.pop();

            R[i] = St.top(); St.push(i);
        }

        while(!St.empty()) St.pop();
        //单调栈处理出以每个点的为左边,区间最小值为对应点对值得最右边界
        St.push(n+1);

        for(int i = n;i>=1;i--)
        {
            while(St.size() > 1 && a[St.top()] > a[i]) St.pop();

            L[i] = St.top(); St.push(i);
        }

        while(!St.empty()) St.pop();

        memset(sumL,0,sizeof(sumL));

        memset(sumR,0,sizeof(sumR));

        sumR[1] =a[1];

        //处理树的前缀和。

        for(int i = 2;i<=n;i++) sumR[i] = sumR[R[i]]+(1LL)*(i-R[i])*a[i];

        sumL[n] = a[n];

        for(int i = n-1;i >= 1;i--) sumL[i] = sumL[L[i]]+(1LL)*(L[i]-i)*a[i];

        LL sum = 0;

        int l = 0,r = 1;

        for(int i = 1;i<=m;i++)
        {
            while(r <= S[i].R) sum +=Rcal(l,r),r++;

            while(r > S[i].R+1) r --,sum-=Rcal(l,r);

            while(l < S[i].L-1) l++,sum-=Lcal(l,r);

            while(l >= S[i].L) sum+=Lcal(l,r),l--;

            ans[S[i].Id] =sum;
        }

        for(int i = 1;i<=m;i++) printf("%lld\n",ans[i]);
    }
    return 0;
}

Codeforces 617E. XOR and Favorite Number


time limit per test4 secondsmemory limit per test256 megabytes

Bob has a favorite number k and ai of length n. Now he asks you to answer m queries. Each query is given by a pair li and ri and asks you to count the number of pairs of integers i and j, such that l ≤ i ≤ j ≤ r and the xor of the numbers ai, ai + 1, …, aj is equal to k.

Input

The first line of the input contains integers n, m and k (1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 1 000 000) — the length of the array, the number of queries and Bob’s favorite number respectively.

The second line contains n integers ai (0 ≤ ai ≤ 1 000 000) — Bob’s array.

Then m lines follow. The i-th line contains integers li and ri (1 ≤ li ≤ ri ≤ n) — the parameters of the i-th query.

Output

Print m lines, answer the queries in the order they appear in the input.

Examples

input
6 2 3
1 2 1 1 0 3
1 6
3 5
output
7
0
input
5 3 1
1 1 1 1 1
1 5
2 4
1 3
output
9
4
4

Note

In the first sample the suitable pairs of i and j for the first query are: (1, 2), (1, 4), (1, 5), (2, 3), (3, 6), (5, 6), (6, 6). Not a single of these pairs is suitable for the second query.

In the second sample xor equals 1 for all subarrays of an odd length.

询问区间中子区间异或值为k的个数。我们如果提前处理出异或前缀那么区间[L,R]的异或的值就为sum[R]^sum[L-1] = k,转化一下,我们就可以知道sum[L-1] = sum[R]^k,sum[R] = sum[L-1]^k,那么区间想右移动一位增加的区间为k的数量就是在[L,R+1]中num[sum[R]^k].那么我们就可以通过莫队算法解决这个问题。

#include <bits/stdc++.h>
#define rr() freopen("in.in","r",stdin)
#define ww() freopen("out.out","w",stdout)

using namespace std;

typedef long long LL;

const int Max=  1<<22;

int num[Max],Bo;

int pre[100100];

typedef struct node
{
    int L,R,Id;

    bool operator < (const node &a)const
    {
        return (L/Bo == a.L/Bo)?R<a.R:(L/Bo<a.L/Bo);
    }
}Section;

Section S[100100];

LL ans[100100];

int main()
{
    int n,data,m,k;

    //rr();

    scanf("%d %d %d",&n,&m,&k);

    Bo = (int)sqrt(n);

    pre[0] = 0;

    for(int i =1;i<=n;i++)
    {
        scanf("%d",&data);

        pre[i] = pre[i-1]^data;
    }

    for(int i = 1;i<=m;i++)
    {
        scanf("%d %d",&S[i].L,&S[i].R);

        S[i].Id = i;
    }

    sort(S+1,S+m+1);

    LL sum = 0;

    int l = 0,r = 0;

    memset(num,0,sizeof(num));

    for(int i = 1;i<=m;i++)
    {
        while(r<=S[i].R)
        {
            sum+=num[pre[r]^k];

            num[pre[r]]++;

            r++;
        }

        while(r>S[i].R+1)
        {
            r--;

            --num[pre[r]];

            sum-=num[pre[r]^k];

        }
        while(l<S[i].L-1)
        {

            --num[pre[l]];

            sum-=num[pre[l]^k];

            l++;

        }

        while(l>=S[i].L)
        {
            l--;

            sum+=num[pre[l]^k];

            num[pre[l]]++;

        }
        ans[S[i].Id] = sum;
    }

    for(int i = 1;i<=m;i++) printf("%I64d\n",ans[i]);
    return 0;
}

Bzoj【1086】—— [SCOI2005]王室联邦


Time Limit: 10 SecMemory Limit: 162 MBSecSpecial Judge
Description

  “余”人国的国王想重新编制他的国家。他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成
员来管理。他的国家有n个城市,编号为1..n。一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条
直接或间接的道路。为了防止管理太过分散,每个省至少要有B个城市,为了能有效的管理,每个省最多只有3B个
城市。每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。但是该省的任意一个城市到达省会所经
过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。一个城市可以作为多个省的省会。聪明的
你快帮帮这个国王吧!

Input

  第一行包含两个数N,B(1<=N<=1000, 1 <= B <= N)。接下来N-1行,每行描述一条边,包含两个数,即这
条边连接的两个城市的编号。

Output

  如果无法满足国王的要求,输出0。否则输出数K,表示你给出的划分方案中省的个数,编号为1..K。第二行输
出N个数,第I个数表示编号为I的城市属于的省的编号,第三行输出K个数,表示这K个省的省会的城市编号,如果
有多种方案,你可以输出任意一种。

Sample Input

8 2

1 2

2 3

1 8

8 7

8 6

4 6

6 5

Sample Output

3

2 1 1 3 3 3 3 2

2 1 8

树上莫队的预备知识,对树进行分块

VFlea King的讲解:首先以任意节点为根DFS,DFS的任务是解决以当前节点为根的子树(不包括当前节点)中的节点的归属,并汇报不知道去向何方者。具体做法是:对于每个正在处理的节点v,定义一个等待序列,扫一遍它的孩子。在扫的时候,假设当前for循环正在处理的是孩子u,DFS(u),然后把传回来的等待序列加到v的等待序列中。如果v的等待序列节点数超过了B,那么就让等待队列中的节点组成一个省,省会是v,但v不划入那个省中。最后,for循环结束,把v加入到等待序列中,return。在主函数中接收DFS的返回值,若等待序列为空,皆大欢喜;但事实上,等待序列不可能为空。那么等待序列中的节点数一定不超过B + 1,怎么办?答案就是放在上一个被划分出的省中去,那么上一个被划分出的省一定<=2B - 1,而现在最后无家可归的节点数一定<= B + 1,所以放在上一个被划分出的省中去节点数一定不超过3B!而且最后被划分出来的省一定和最后无家可归的节点中的某一个相邻!”

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

int n,B,num;

int pre[1100];

int a[1100],b[1100];

vector<int>E[1100];

void Init(){
    for(int i =1;i<=n;i++) E[i].clear();
}

int dfs(int u,int fa){

    int size = 0;

    for(int i = 0;i<E[u].size();i++)
    {
        if(E[u][i] == fa) continue;

        size += dfs(E[u][i],u);

        if(size >=B){
            ++num;

            for(int i = a[0];i>a[0]-size;i--){
                pre[a[i]] = num;
            }
            b[++b[0]] = u;

            a[0] -=size;

            size = 0;
        }
    }

    size ++;

    a[++a[0]] = u;

    return size;
}

int main()
{
    int u,v;

    while(~scanf("%d %d",&n,&B)){
        Init();

        for(int i = 1;i<n;i++){
            scanf("%d %d",&u,&v);

            E[u].push_back(v);

            E[v].push_back(u);
        }

        num = 0;

        a[0] = 0; b[0] = 0;

        int ans = dfs(1,0);

        if(ans) {
            for(int i = 1;i<=a[0];i++)
            {
                pre[a[i]] = num;
            }
        }

        printf("%d\n",num);


        if(num){
            for(int i =1;i<=n;i++) {
                if(i!=1) printf(" ");
                printf("%d",pre[i]);
            }

            printf("\n");

            for(int i = 1;i<=b[0];i++) {
                if(i != 1) printf(" ");
                printf("%d",b[i]);
            }

            printf("\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值