2018.12.16四校联考T3(cdq分治,set)

题目大意
给一个 n n n个正整数的序列
共有 m m m次操作
操作 1 1 1:将区间 [ l , r ] [l,r] [l,r]中所有数变为 x x x
操作 2 2 2:询问区间 [ l , r ] [l,r] [l,r]中不同种类数的个数
n , m ≤ 1 0 5 , a i ≤ 1 0 9 n,m\le 10^5,a_i\le 10^9 n,m105,ai109


s o l u t i o n solution solution:
(分析复杂度视 n , m n,m n,m同数量级
显然数要离散掉

没有操作 1 1 1或者操作 1 1 1 l = r l=r l=r我们都可以直接做了(待修改莫队,树套树, c d q cdq cdq分治)

直接考虑树套树以及 c d q cdq cdq分治的做法
如果没有操作 1 1 1,我们记录一个 p r e [ i ] pre[i] pre[i]表示上一个与 a i a_i ai相同的位置
那么询问 [ l , r ] [l,r] [l,r]其实就是询问对于所有 x ∈ [ l , r ] , x∈[l,r], x[l,r],满足 p r e [ x ] &lt; l pre[x]&lt;l pre[x]<l的个数
裸的二维数点。

单点修改我们直接用 s e t set set维护前驱后继就做完了

区间修改?

我们可以发现对于一段相同的数把他变为另一个数会变化的只有这一段数的头和尾
考虑用 s e t set set维护每一段相同的块

每次修改要做的只是将 l l l r r r所在的块分裂,并把中间的块删去,添加一个新的块

可以发现块的个数是 O ( n ) O(n) O(n)的,故这一部分复杂度为 n l o g n nlogn nlogn
后面就是裸的 c d q cdq cdq分治了

总复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define maxn 100010
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}

int n,m,t;
int ans[101000],lk,pre[101000];
int st[101000],pl[101000];

namespace cdq
{
	int tr[101000];
	void add(int x,int v){for(x++;x<=maxn;x+=x&-x) tr[x] += v;}
	int ask(int x){int now=0;for(x++;x;x-=x&-x) now+=tr[x];return now;}
	struct data{int op,x,y,val;}a[2001000],tmp[2001000];
	void cdq(int l,int r)
	{
		if(l == r) return;
		int mid = (l+r)>>1;
		cdq(l,mid);cdq(mid+1,r);
		int p = l,q = mid+1,t = 0;
		while(p <= mid && q <= r)
		    if(a[p].x <= a[q].x)
			{
		    	if(a[p].op == 1) add(a[p].y,a[p].val);
		    	tmp[++t] = a[p++];
		    }
		    else
		    {
		    	if(a[q].op == 2) ans[a[q].val] -= ask(a[q].y);
		    	if(a[q].op == 3) ans[a[q].val] += ask(a[q].y);
		    	tmp[++t] = a[q++];
		    }
		while(q <= r) 
		{
			if(a[q].op == 2) ans[a[q].val] -= ask(a[q].y);
			if(a[q].op == 3) ans[a[q].val] += ask(a[q].y);
			tmp[++t] = a[q++];
		}
		rep(i,l,p-1) if(a[i].op == 1) add(a[i].y,-a[i].val);
		while(p <= mid) tmp[++t] = a[p++];
		rep(i,l,r) a[i] = tmp[i-l+1];
	}
	void add(int op,int x,int y,int val){a[++t].op = op;a[t].x = x;a[t].y = y;a[t].val = val;}
	
}using namespace cdq;
#define hash Hash
namespace hash
{
	int cnt = 0;
	unordered_map<int,int>g;
	int getid(int x){if(!g[x]) return g[x]=++cnt;else return g[x];}
}using namespace hash;
namespace bwork
{
	#define IT1 set<block>::iterator
	#define IT2 set<int>::iterator
	struct block{
	    int l,r,v;

	    bool operator < (const block &x)const {
	    	return l < x.l;
	    }
	};
	set<block>all,sp[201000];//all存所有颜色块,sp存单个颜色 
	set<int>tmp;//tmp存某次修改要修改的序列
	int cas;
	IT1 getblock(set<block> &x,int pos)
	{
		return --x.upper_bound( (block){pos,0,0} );
	}
	void insert(int l,int r,int v)
	{
		block now = (block){l,r,v};
		all.insert(now);
		sp[v].insert(now);
	}
	void del(IT1 it)
	{
		sp[it->v].erase(getblock(sp[it->v],it->l));
		all.erase(it);
	}
	IT1 spt(int pos)
	{
		if(pos > n) return all.end();
		IT1 it = getblock(all,pos);
		int l = it->l,r = it->r,v = it->v;
		if(pos == l) return it;
		del(it);
		insert(l,pos-1,v);
		insert(pos,r,v);
		return getblock(all,pos);
	}
	int get_ne(int pos)
	{
		IT1 it = getblock(all,pos);
		int v = it->v;
		it = getblock(sp[v],pos);
		if(++it == sp[v].end()) return 0;
		else return it->l;
	}
	int get_pre(int pos)
	{
		IT1 it = getblock(all,pos);
		int v = it->v;
		it = getblock(sp[v],pos);
		if(pos > it->l) return pos-1;
		if(it == sp[v].begin()) return 0;
		return ((--it)->r);
	}
	void change(int x)
	{
		int la = get_pre(x);
		if(la == pre[x]) return;
		if(pre[x] != -1) cdq::add(1,x,pre[x],-1);
		cdq::add(1,x,pre[x] = la,1);
	}
	void update(int l,int r,int v)
	{
		IT1 en = spt(r+1),st = spt(l);
		for(IT1 it = st;it != en;it++)
		{
			tmp.insert(it->l),tmp.insert(get_ne(it->r));
	    }
		for(IT1 it = st;it != en;del(it++));
		insert(l,r,v);
		tmp.insert(get_ne(r));
		for(IT2 it = tmp.begin();it != tmp.end();it++) if((*it) != 0) change((*it));
	    tmp.clear();
	}
}using namespace bwork;
void init()
{
	n = rd();m = rd();
	rep(i,1,n) st[i] = hash::getid(rd());
	rep(i,1,n) if(!pl[st[i]]) pre[i] = 0,pl[st[i]] = i;
	           else pre[i] = pl[st[i]],pl[st[i]] = i;
	rep(i,1,n) cdq::add(1,i,pre[i],1);
	int now = 1;
	rep(i,2,n)
	    if(st[i] != st[i-1]) bwork::insert(now,i-1,st[i-1]),now = i;
	bwork::insert(now,n,st[n]);
}
int main()
{
	freopen("third.in","r",stdin);
	freopen("third.out","w",stdout);
	init();
	rep(i,1,m)
	{
		int op = rd(),l = rd(),r = rd(),x;
		if(op == 2) cdq::add(2,l-1,l-1,++lk),cdq::add(3,r,l-1,lk);
		else x = getid(rd()),bwork::update(l,r,x);
	}
	cdq::cdq(1,t);
	rep(i,1,lk) printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值