Jeanny
寂兮,寥兮,独立不改,周行而不殆

【题目】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。

【方法】

树链剖分

posted on 2022-05-14 09:01  Jeanny  阅读(92)  评论(0)    收藏  举报