好像应该是按照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;
}