好像又有一个星期没更博客了。。

  最近疯狂考试。。。唯一有点收获的就是学会了莫队这种神奇的算法。。

  听起来很难。。其实是一个很简单的东西。。

  就是在区间处理问题时对于一个待求区间[L',R']通过之前求出的[L,R]更新[L,R+1],[L+1,R],[L,R-1],[L,R-1]的方式弄出答案[L,R]。

  比如求【3,5】 我们知道了【1,7】,那么我们这样转化 : 【1,7】--> 【2,7】--> 【3,7】 --> 【3,6】 --> 【3,5】而求得。

  那怎么确定从哪个区间转移呢?

  在这里我们可以把区间左端点和右端点排个序,然后全局变量L,R代表当前答案区间,ans代表当前答案。。每次对于一个新询问慢慢转移即可。

  但是这样貌似还是太暴力了。。

  有两个优化:1) 二维曼哈顿生成树,太难写。。不推荐。

  2)分块。把区间左端点分块处理。每次处理一个块。

  关于分块复杂度的证明http://blog.csdn.net/bossup/article/details/39236275

  其中每次转移可能是O(1)或者O(logn)。

  下面列出一些题目。。


 

  BZOJ 3781 小B的袜子

  

3781: 小B的询问

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 309  Solved: 205
[Submit][Status][Discuss]

Description

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

 

Input

第一行,三个整数N、M、K。
第二行,N个整数,表示小B的序列。
接下来的M行,每行两个整数L、R。

 

Output

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

Sample Input

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

Sample Output

6
9
5
2

HINT

对于全部的数据,1<=N、M、K<=50000


  基础的莫队算法。

  在这里主要说说转移

  一般大家的转移应该都是ans-=num[a[i]]*num[a[i]],num[a[i]]++,ans+=num[a[i]]*num[a[i]].这样大约是2400ms左右

  这里说种更好的转移:ans+=num[a[i]++]*2+1.这样大约可以跑到1200ms左右。。

  其他差不多。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5  
 6 using namespace std;
 7  
 8 #define maxn 50001
 9  
10 int n,cnt=1,team[maxn],num[maxn],a[maxn];
11  
12 long long up[maxn],down[maxn];
13  
14 struct ed{
15     int l,r,id;
16 }edge[maxn];
17  
18 bool cmp(const ed A,const ed B)
19 {
20     if(team[A.l]==team[B.l])
21     return A.r<B.r;
22     return team[A.l]<team[B.l];
23 }
24  
25 void build()
26 {
27     int T=sqrt(n);
28     for(int i=1;i<=n;i++)
29     {
30     if(i%T==0)cnt++;
31     team[i]=cnt;
32     }
33 }
34  
35 long long gcd(long long nn,long long mm){return nn%mm==0?mm:gcd(mm,nn%mm);}
36  
37 inline int read()
38 {
39 int x=0;char ch=getchar();
40 while(ch<'0'||ch>'9')ch=getchar();
41 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
42 return x;
43 }
44  
45 int main()
46 {
47     int m,k;
48     n=read(),m=read(),k=read();
49     for(int i=1;i<=n;i++)a[i]=read();
50     build();
51     for(int i=1;i<=m;i++)
52     {
53     edge[i].l=read(),edge[i].r=read();
54     edge[i].id=i;
55     }
56     sort(1+edge,1+edge+m,cmp);
57     int ll=1,lr=0;
58     long long ans=0;
59     for(int i=1;i<=m;i++)
60     {
61     if(edge[i].l==edge[i].r)
62     {
63         up[edge[i].id]=1,down[edge[i].id]=1;
64         continue;
65     }
66     if(lr<edge[i].r)
67         {
68         for(int j=lr+1;j<=edge[i].r;j++)
69         ans+=num[a[j]]*2+1,num[a[j]]++;
70     }
71     else
72     {
73         for(int j=lr;j>edge[i].r;j--)
74         ans-=(--num[a[j]])*2+1;
75     }
76     lr=edge[i].r;
77     if(ll>edge[i].l)
78     {
79         for(int j=ll-1;j>=edge[i].l;j--)
80         ans+=num[a[j]]*2+1,num[a[j]]++;
81     }
82     else
83     {
84         for(int j=ll;j<edge[i].l;j++)
85         ans-=(--num[a[j]])*2+1;
86     }
87     ll=edge[i].l;
88     up[edge[i].id]=ans;
89     }
90     for(int i=1;i<=m;i++)
91     printf("%lld\n",up[i]);
92     return 0;
93 }
View Code

 

相关文章:

  • 2022-01-20
  • 2021-07-18
  • 2022-01-25
  • 2022-01-06
  • 2021-10-29
  • 2021-10-09
  • 2021-12-01
  • 2022-12-23
猜你喜欢
  • 2021-10-15
  • 2022-03-08
  • 2022-12-23
  • 2022-12-23
  • 2022-02-11
  • 2021-10-19
  • 2021-08-12
相关资源
相似解决方案