【题目】SP3267 DQUERY - D-query(https://www.luogu.com.cn/problem/SP3267)
【问题描述】
给出一个长度为n 的数列,需要你给出[L,R]这一段中有多少不同的数字。
【方法1】
莫队
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxx = 3e5+5;
int a[maxx], vis[maxx], ans[maxx], block, res;
struct node{
int l,r;
int id;
bool operator < (const node &b)const{
if (l/block == b.l/block){
return r < b.r;
}
return l/block < b.l/block;
}
}que[maxx];
void add(int x){
vis[a[x]]++;
if(vis[a[x]]==1)
res++;
}
void del(int x){
vis[a[x]]--;
if(vis[a[x]]==0){
res--;
}
}
int main(){
int n,q;
scanf("%d",&n);
res=0; block=sqrt(n);//一块有多长
memset(vis,0,sizeof(vis));//初始化为0
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
}
scanf("%d",&q);
for (int i=1; i<=q; i++){
scanf("%d%d",&que[i].l,&que[i].r);
que[i].id=i;
}
sort(que+1, que+1+q);
int l=1,r=0;
for (int i=1; i<=q; i++){
while(l < que[i].l) del(l++);
while(l > que[i].l) add(--l);//询问的更小,即在左边,则add
while(r < que[i].r) add(++r);
while(r > que[i].r) del(r--);
ans[que[i].id] = res;
}
for (int i=1; i<=q; i++){
if(i-1)printf(" %d",ans[i]);
else printf("%d",ans[i]);
}
return 0;
}
/*
6
1 3 2 1 1 2
3
1 6
3 6
4 5
*/
【方法2】
以颜色为下标,再加一个离散化最好
//talk is cheap, show your code
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int CNT, n, rt[30005], c, q;
struct Tree{
int l,r,val,fl;
}tr[N*21*2];
void build(int &now, int l, int r){
now = ++CNT;
tr[now].val = 0;
if(l == r) return;
int mid = (l + r)>>1;
build(tr[now].l, l, mid);
build(tr[now].r, mid+1, r);
}
void add(int &now, int pre, int l, int r, int pos){
now = ++CNT;
tr[now] = tr[pre];
tr[now].val++;
if(l == r) return;
int mid = (l+r)>>1;
if(pos <= mid) add(tr[now].l, tr[pre].l, l, mid, pos);
else add(tr[now].r, tr[pre].r, mid+1, r, pos);
}
void pushup(int rt){
tr[rt].fl = tr[tr[rt].l].fl + tr[tr[rt].r].fl;
}
void split(int &now, int ql, int qr, int l, int r){
now = ++CNT;
if(l == r){
tr[now].val = tr[qr].val - tr[ql].val;
return;
}
int mid = (l + r)>>1;
split(tr[now].l, tr[ql].l, tr[qr].l, l, mid);
split(tr[now].r, tr[ql].r, tr[qr].r, mid+1, r);
}
void query(int now, int l, int r){
if(l == r){
if(tr[now].val) tr[now].fl = 1;
return;
}
int mid = (l+r)>>1;
query(tr[now].l, l, mid);
query(tr[now].r, mid+1, r);
pushup(now);
}
int main(){
scanf("%d",&n);
build(rt[0], 1, n);
for(int i = 1; i <= n; i++){
scanf("%d",&c);
add(rt[i], rt[i-1], 1, n, c);
}
scanf("%d",&q); int x,y;
for(int i = 1; i <= q; i++){
scanf("%d%d",&x,&y);
int tmp;
split(tmp, rt[x-1], rt[y], 1, n);
query(tmp, 1, n);
printf("%d\n",tr[tmp].fl);
}
return 0;
}
【方法3】
二维树状数组?
【题目】U41492 树上数颜色
【问题描述】
给一棵根为1的树,每次询问子树颜色种类数。1<=m,c[i]<=n<=1e5
【方法1】
dsu on tree
【方法2】
dfs序列 + 莫队
【题目】P1558 色板游戏
【问题描述】
色板长度为 L,C A B C 指在 A 到 B 号方格中涂上颜色 C,P A B 指老师的提问:A 到 B 号方格中有几种颜色。1 <= L <= 100000,颜色最多30种。
【方法1】
线段树。由于30种不同的颜色,可以开30棵线段树表示30种颜色,每棵线段树的下标是dfs序列编号。如果某一区间涂成x颜色,那么对用第x棵线段树对应的区间都更改为1。那么其他线段树对应的区间颜色涂成0。
【方法2】
线段树。开一棵线段树,将颜色转换为二进制数的状压形式。如果有第x种颜色则用1<<(x-1)表达。上传的时候,或运算。
"tree[rt] = tree[rt<<1] | tree[rt<<1 | 1];"
【题目】CF620E New Year Tree
【问题描述】
将上一题改成树,区间改成子树。颜色60种。
【方法1】
dfs序列+线段树+二进制压缩。
【题目】P1903 [国家集训队] 数颜色 / 维护队列
【问题描述】
Q L R 代表询问你从第 L支画笔到第 R 支画笔中共有几种不同颜色的画笔。
R P Col 把第 P 支画笔替换为颜色 Col。对于所有数据,n,m≤133333
【方法】
带修莫队
【题目】P2486 [SDOI2011]染色
【问题描述】
将节点 a 到节点 b 的路径上的所有点(包括 a和 b)都染成颜色c。
询问节点 a 到节点 b的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:11、222、1。
【方法】
树链剖分
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架