莫队算法:

好像应该是按照n分块,下面有很多都是按照m分的块。
遇到具体问题再具体分析吧~~

日常%一发mmh学长。
实在是太强了,一直想跟着马学长的步伐前进。
优雅的暴力。
直接上例题了。
例①:P2709 小B的询问:

小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L…R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L…R]中的重复次数。小B请你帮助他回答询问。

输入格式
第一行,三个整数N、M、K。

第二行,N个整数,表示小B的序列。

接下来的M行,每行两个整数L、R。

输出格式
M行,每行一个整数,其中第i行的整数表示第i个询问的答案。

输入输出样例
输入 #1 复制
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6
输出 #1 复制
6
9
5
2
说明/提示
对于全部的数据,1<=N、M、K<=50000

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const double eps = 1e-8;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=50010;
const int mod=1e9+7;
ll sum[maxn],ans[maxn],res=0;
struct node
{
    int l,r,id;
}a[maxn];
int c[maxn],pos[maxn];

bool cmp(const node &a,const node &b)
{
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    else if(pos[a.l]&1) return a.r<b.r;
    else return a.r>b.r;
}

void del(int x)
{
    res-=2*sum[c[x]]-1;
    sum[c[x]]--;
}

void add(int x)
{
    res+=2*sum[c[x]]+1;
    sum[c[x]]++;
}

int main(void)
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);

    int t=sqrt(m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&c[i]),pos[i]=(i-1)/t+1;

    for(int i=1;i<=m;i++)
        scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;

    sort(a+1,a+m+1,cmp);

    int ql=1,qr=0;
    for(int i=1;i<=m;i++)
    {
        int nowl=a[i].l,nowr=a[i].r;
        while(ql<nowl) del(ql),ql++;
        while(ql>nowl) ql--,add(ql);
        while(qr<nowr) qr++,add(qr);
        while(qr>nowr) del(qr),qr--;
        ans[a[i].id]=res;
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

例②:P1494 [国家集训队]小Z的袜子:

题目描述
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……

具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。

你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

然而数据中有L=R的情况,请特判这种情况,输出0/1。

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

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

输入输出样例
输入 #1 复制
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
输出 #1 复制
2/5
0/1
1/1
4/15
说明/提示
30%的数据中 N,M ≤ 5000;

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

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

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const double eps = 1e-8;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=50010;
const int mod=1e9+7;
ll sum[maxn],ans1[maxn],ans2[maxn],res=0;
struct node
{
    int l,r,id;
}a[maxn];
int c[maxn],pos[maxn];

bool cmp(const node &a,const node &b)
{
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    else if(pos[a.l]&1) return a.r<b.r;
    else return a.r>b.r;
}

void add(int x)
{
    sum[x]++;
    res=res-(sum[x]-1)*(sum[x]-2)+sum[x]*(sum[x]-1);
}

void del(int x)
{
    sum[x]--;
    res=res-(sum[x]+1)*sum[x]+(sum[x]-1)*sum[x];
}

ll gcd(ll a,ll b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}

int main(void)
{
    int n,m;
    scanf("%d%d",&n,&m);

    int t=sqrt(m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&c[i]),pos[i]=(i-1)/t+1;

    for(int i=1;i<=m;i++)
        scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;

    sort(a+1,a+m+1,cmp);

    int ql=1,qr=0;
    for(int i=1;i<=m;i++)
    {
        int nowl=a[i].l,nowr=a[i].r;
        if(nowl==nowr)
        {
            ans1[a[i].id]=0;
            ans2[a[i].id]=1;
            continue;
        }
        while(ql<nowl) del(c[ql]),ql++;
        while(ql>nowl) ql--,add(c[ql]);
        while(qr<nowr) qr++,add(c[qr]);
        while(qr>nowr) del(c[qr]),qr--;
        ans1[a[i].id]=res;
        ans2[a[i].id]=(nowr-nowl)*(ll)(nowr-nowl+1);
    }
    for(int i=1;i<=m;i++)
    {
        ll d=gcd(ans2[i],ans1[i]);
        printf("%lld/%lld\n",ans1[i]/d,ans2[i]/d);
    }

    return 0;
}

例③:P1903 [国家集训队]数颜色 / 维护队列:
带修改莫队:

题目描述
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。

2、 R P Col 把第P支画笔替换为颜色Col。

为了满足墨墨的要求,你知道你需要干什么了吗?

输入格式
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。

第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。

第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

输出格式
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

输入输出样例
输入 #1 复制
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
输出 #1 复制
4
4
3
4
说明/提示
对于30%的数据,n,m \leq 10000n,m≤10000

对于60%的数据,n,m \leq 50000n,m≤50000

对于所有数据,n,m \leq 133333n,m≤133333

所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

本题可能轻微卡常数

来源:bzoj2120

本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。
原来的代码好像写错了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const double eps = 1e-8;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=1000010;
const int mod=1e9+7;
int cnt[maxn],c[maxn],cc[maxn],ans[maxn],pos[maxn],res=0;
struct node
{
    int l,r,id,k;
}a[maxn];
struct nn
{
    int x,y,z;
}b[maxn];
int ql=1,qr=0,k=0;


bool cmp(const node &a,const node &b)
{
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    else if(pos[a.r]!=pos[b.r]) return pos[a.r]<pos[b.r];
    else return a.k<b.k;
}

void change_add(int k)
{
    if(ql<=b[k].x&&b[k].x<=qr)
    {
        cnt[c[b[k].x]]--;
        if(cnt[c[b[k].x]]==0) res--;
        cnt[b[k].y]++;
        if(cnt[b[k].y]==1) res++;
    }
    c[b[k].x]=b[k].y;
}

void change_del(int k)
{
    if(ql<=b[k].x&&b[k].x<=qr)
    {
        cnt[c[b[k].x]]--;
        if(cnt[c[b[k].x]]==0) res--;
        cnt[b[k].z]++;
        if(cnt[b[k].z]==1) res++;
    }
    c[b[k].x]=b[k].z;
}

void add(int x)
{
    cnt[x]++;
    if(cnt[x]==1) res++;
}

void del(int x)
{
    cnt[x]--;
    if(cnt[x]==0) res--;
}



int main(void)
{
    int n,m;
    char op[20];
    int x,y;

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

    int t=pow(n,2.0/3);
    for(int i=1;i<=n;i++)
        scanf("%d",&c[i]),cc[i]=c[i],pos[i]=(i-1)/t+1;

    int cnta=0,cntb=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%s%d%d",op,&x,&y);
        if(op[0]=='Q')
        {
            cnta++;
            a[cnta].l=x,a[cnta].r=y,a[cnta].id=cnta,a[cnta].k=cntb;
        }
        else
        {
            cntb++;
            b[cntb].x=x,b[cntb].y=y,b[cntb].z=cc[x],cc[x]=y;
        }
    }

    sort(a+1,a+cnta+1,cmp);


    for(int i=1;i<=cnta;i++)
    {
        int nowl=a[i].l,nowr=a[i].r,nowk=a[i].k;
        while(ql<nowl) del(c[ql]),ql++;
        while(ql>nowl) ql--,add(c[ql]);
        while(qr<nowr) qr++,add(c[qr]);
        while(qr>nowr) del(c[qr]),qr--;
        while(k<nowk) k++,change_add(k);
        while(k>nowk) change_del(k),k--;
        ans[a[i].id]=res;
    }
    for(int i=1;i<=cnta;i++)
    {
        printf("%d\n",ans[i]);
    }

    return 0;
}

例④:Chika and Friendly Pairs HDU - 6534 :

莫队加树状数组

Chika gives you an integer sequence a1,a2,…,an and m tasks. For each task, you need to answer the number of " friendly pairs" in a given interval.

friendly pair: for two integers ai and aj, if i<j and the absolute value of ai−aj is no more than a given constant integer K, then (i,j) is called a “friendly pair”.A friendly pair (i,j) in a interval [L,R] should satisfy L≤i<j≤R.
Input
The first line contains 3 integers n (1≤n≤27000), m (1≤m≤27000) and K (1≤K≤109), representing the number of integers in the sequence a, the number of tasks and the given constant integer.
The second line contains n non-negative integers, representing the integers in the sequence a. Every integer of sequence a is no more than 109.
Then m lines follow, each of which contains two integers L, R (1≤L≤R≤n). The meaning is to ask the number of “friendly pairs” in the interval [L,R]。
Output
For each task, you need to print one line, including only one integer, representing the number of “friendly pairs” in the query interval.
Sample Input
7 5 3
2 5 7 5 1 5 6
6 6
1 3
4 6
2 4
3 4
Sample Output
0
2
1
3
1

题意:给你n,m,k。然后输入n个数,m个区间。然后问你每个区间满足|a[i]- a[j]|<=k的个数(i<=j)。

a [ i ] - k <= a [ j ] <= a [ i ] + k

莫队解决,树状数组求贡献。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const double eps = 1e-8;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=30010;
const int mod=1e9+7;
ll a[maxn*3],b[maxn*3],res=0,ans[maxn],pos[maxn],c[maxn*3];
ll n,m,k;
struct node
{
    int l,r,id;
}q[maxn];

bool cmp(const node &a,const node &b)
{
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    else if(pos[a.l]&1) return a.r<b.r;
    else return a.r>b.r;
}

void add(int x,int val)
{
    for(;x<maxn*3;x+=(x&-x))
        c[x]+=val;
}

ll ask(int x)
{
    ll ans=0;
    for(;x;x-=(x&-x))
        ans+=c[x];
    return ans;
}



void add(int x)
{
    res+=ask(a[x+n*2])-ask(a[x+n]-1);
    add(a[x],1);
}

void del(int x)
{
    add(a[x],-1);
    res-=ask(a[x+n*2])-ask(a[x+n]-1);
}



int main(void)
{
    while(scanf("%lld%lld%lld",&n,&m,&k)!=EOF)
    {
        int t=sqrt(m);
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]),pos[i]=(i-1)/t+1;
            a[i+n]=a[i]-k,a[i+n*2]=a[i]+k;
            b[i]=a[i],b[i+n]=a[i+n],b[i+n*2]=a[i+n*2];
        }
        sort(b+1,b+n*3+1);
        int cnt=unique(b+1,b+n*3+1)-(b+1);
        for(int i=1;i<=n*3;i++)
            a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;

        for(int i=1;i<=m;i++)
            scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
        sort(q+1,q+m+1,cmp);

        int ql=1,qr=0;
        res=0;
        for(int i=1;i<=m;i++)
        {
            int nowl=q[i].l,nowr=q[i].r;
            while(ql<nowl) del(ql),ql++;
            while(ql>nowl) ql--,add(ql);
            while(qr<nowr) qr++,add(qr);
            while(qr>nowr) del(qr),qr--;
            ans[q[i].id]=res;
        }
        for(int i=1;i<=m;i++)
        {
            printf("%lld\n",ans[i]);
        }
    }
    return 0;
}

例⑤:SP10707 COT2 - Count on a tree II:

题意翻译
题目描述
给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数。

输入格式
第一行有两个整数n和m(n=40000,m=100000)。

第二行有n个整数。第i个整数表示第i个节点表示的整数。

在接下来的n-1行中,每行包含两个整数u v,描述一条边(u,v)。

在接下来的m行中,每一行包含两个整数u v,询问u到v的路径上有多少个不同的整数。

输出格式
对于每个询问,输出结果。 贡献者:つるまる

题目描述
You are given a tree with N nodes. The tree nodes are numbered from 1 to N. Each node has an integer weight.

We will ask you to perform the following operation:

u v : ask for how many different integers that represent the weight of nodes there are on the path from u to v.
输入格式
In the first line there are two integers N and M. (N <= 40000, M <= 100000)

In the second line there are N integers. The i-th integer denotes the weight of the i-th node.

In the next N-1 lines, each line contains two integers u v, which describes an edge (u, v).

In the next M lines, each line contains two integers u v, which means an operation asking for how many different integers that represent the weight of nodes there are on the path from u to v.

输出格式
For each operation, print its result.

输入输出样例
输入 #1 复制
8 2
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5
7 8
输出 #1 复制
4
4

用倍增写没有过,换了树剖求lca就过了。是不是我倍增又写错了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const double eps = 1e-8;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100010;
const int mod=1e9+7;
int head[maxn],ver[maxn],nt[maxn];
int st[maxn],ed[maxn],sum[maxn];
int a[maxn],b[maxn],pos[maxn],ans[maxn],res=0;
bool ha[maxn];
struct node
{
    int l,r,id,lca;
}q[maxn];
int d[maxn],f[maxn],son[maxn],si[maxn];
int rk[maxn],top[maxn];
int tot=1,cnt=0,cm=0;

void add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}

void dfs1(int x,int fa)
{
    int max_son=0;
    si[x]=1;st[x]=++cm;rk[cm]=x;
    for(int i=head[x];i;i=nt[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        d[y]=d[x]+1;
        f[y]=x;
        dfs1(y,x);
        si[x]+=si[y];
        if(si[y]>max_son) max_son=si[y],son[x]=y;
    }
    ed[x]=++cm;rk[cm]=x;
}

void dfs2(int x,int t)
{
    top[x]=t;
    if(!son[x]) return ;
    dfs2(son[x],t);
    for(int i=head[x];i;i=nt[i])
    {
        int y=ver[i];
        if(y!=son[x]&&y!=f[x])
            dfs2(y,y);
    }
}

int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]]) swap(x,y);
        x=f[top[x]];
    }
    return d[x]>d[y]?y:x;
}

bool cmp(const node &a,const node &b)
{
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    else if(pos[a.l]&1) return pos[a.r]<pos[b.r];
    else return pos[a.r]>pos[b.r];
}

void del(int x)
{
    sum[x]--;
    if(sum[x]==0) res--;
}

void add(int x)
{
    sum[x]++;
    if(sum[x]==1) res++;
}

void cal(int x)
{
    if(ha[x]) del(a[x]);
    else add(a[x]);
    ha[x]^=1;
}



int main(void)
{
    int n,m;
    int x,y;
    scanf("%d%d",&n,&m);
    int len=n*2.0/sqrt(m);

    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),b[i]=a[i];
    for(int i=1;i<=n*2;i++)
        pos[i]=(i-1)/len+1;

    sort(b+1,b+n+1);
    int cnt=unique(b+1,b+n+1)-(b+1);
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;

    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);


    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if(st[x]>st[y]) swap(x,y);
        q[i].id=i,q[i].lca=lca(x,y);
        if(q[i].lca==x)
            q[i].l=st[x],q[i].r=st[y],q[i].lca=0;
        else q[i].l=ed[x],q[i].r=st[y];
    }

    sort(q+1,q+m+1,cmp);
    int l=1,r=0;
    for(int i=1;i<=m;i++)
    {
        int nowl=q[i].l,nowr=q[i].r;
        while(l<nowl) cal(rk[l]),l++;
        while(l>nowl) l--,cal(rk[l]);
        while(r<nowr) r++,cal(rk[r]);
        while(r>nowr) cal(rk[r]),r--;
        if(q[i].lca) cal(q[i].lca);
        ans[q[i].id]=res;
        if(q[i].lca) cal(q[i].lca);
    }

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

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值