【洛谷】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支画笔中共有几种不同颜色的画笔。

题意:如题

思路:

带修莫队,多加一维时间截。算法细节见代码注释。

AC代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e6+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline int read(){ int f = 1; int x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

int a[maxn];
int pos[maxn];
int cnt[maxn];
int Ans[maxn];
int n, m;
int sum = 0;

typedef struct Time     //记录每个时间截的修改
{
    int p;
    int col;
}T;
T c[maxn];

typedef struct Query    //记录查询
{
    int l;
    int r;
    int t;
    int id;
}Q;
Q q[maxn];

bool cmp(Q a , Q b)     //三个键的比较
{
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    if(pos[a.r]!=pos[b.r])return ( (pos[a.l] &1 )?pos[a.r] < pos[b.r] : pos[a.r] > pos[b.r] );
    return a.t<b.t;
}

inline void add(int x)  
{
    if(!cnt[x]) ++sum;
    ++cnt[x] ;
}

inline void del(int x)      //这两个函数和普通莫队一样
{
    --cnt[x] ;
    if(!cnt[x]) --sum;
}

inline void modify(int x, int ti)         //对于当前时间截的修改
{
    if(c[ti].p >= q[x].l && c[ti].p <= q[x].r) del(a[c[ti].p]), add(c[ti].col);     //如果当前时刻要修改的位置在本次区间中,那么就去掉原先的颜色,添加进要改的颜色
    swap(a[c[ti].p], c[ti].col);        //两者交换一下,因为下一次回到这个时刻肯定又换回来了。
}

int main()
{
    int n = read(), m = read();
    int block = pow(n,2/3.0);
    rep(i,1,n) a[i] = read(), pos[i] = (i-1)/block + 1;
    int p1 = 0, p2 = 0;     //p1记录查询个数, p2记录修改个数
    rep(i,1,m)
    {
        char s[5]; scanf("%s",s);
        if(s[0] == 'Q') q[++p1].l = read(), q[p1].r = read(), q[p1].t = p2, q[p1].id = p1;      //同时记录当前时间截
        else c[++p2].p = read(), c[p2].col = read();
    }
    sort(q+1,q+1+p1,cmp);       //排序
    int now = 0; int L = 0, R = 0;
    rep(i,1,p1)
    {
        while(q[i].l < L) add(a[--L]);
        while(q[i].r > R) add(a[++R]);
        while(q[i].l > L) del(a[L++]);
        while(q[i].r < R) del(a[R--]);      //上面四个和普通莫队一样
        while(now < q[i].t) modify(i,++now);        //增加到当前时间截,从下一个时刻开始修改
        while(now > q[i].t) modify(i,now--);        //回溯到当前时间截,从当前开始一个个修改回去
        Ans[q[i].id] = sum;
    }
    rep(i,1,p1) printf("%d\n",Ans[i]);
    return 0;
}

©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页