Tulip Festival(线段树+二分+CDQ+带修改莫队+树套树)
题目链接
线段树\(+\)二分思路
思路
比赛看到这题时感觉是一棵线段树\(+\)主席树,然后因为不会带修改主席树就放弃了,最后发现还卡了树套树。
由于本题数据保证序列中相同的数字不会超过200个,因此我们将每个读入的数和修改之后的数一起离散化一遍,然后用一个\(vector\)记录每个数出现的下标,然后用线段树维护区间异或值。查询时就先查询得到的这个异或值\(num\)是否在离散化中出现过,不出现那么一定不会有\(a_i\)等于\(num\),此时答案就是\(R-L+1\);否则我们就在\(num\)对应的\(vector\)中进行二分求出\(L\)与\(R\)间的数的个数即可。但是比赛的时候我最后查询时忘记找到它对应离散化后的值然后一直\(RE\),然后和队友一起找了半个多小时也没找到,不然就六题了,成功背了一大锅。
代码实现如下
#include <bits/stdc++.h>
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define bug printf("*******\n");
#define debug(x) cout<<"["<<x<<"]"<<endl;
#define name2str(x) #x;
#define fuck(x) cout<<#x" = "<<x<<endl
typedef long long LL;
typedef pair<int, int> pii;
typedef pair<LL, int> pLi;
const int maxn = 1e6 + 7;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double pi = acos ( -1 );
int n, q;
int a[maxn];
vector<int> v, G[maxn + 100007];
vector<int>::iterator it;
struct pp {
int op, l, r;
} ask[100007];
struct node {
int l, r, num;
} segtree[maxn << 2];
void push_up ( int rt ) {
segtree[rt].num = ( segtree[lson].num ^ segtree[rson].num );
}
void build ( int rt, int l, int r ) {
segtree[rt].l = l, segtree[rt].r = r;
segtree[rt].num = 0;
if ( l == r ) {
segtree[rt].num = a[l];
return;
}
int mid = ( l + r ) >> 1;
build ( lson, l, mid );
build ( rson, mid + 1, r );
push_up ( rt );
}
void update ( int rt, int pos, int x ) {
if ( segtree[rt].l == segtree[rt].r ) {
segtree[rt].num = x;
return;
}
int mid = ( segtree[rt].l + segtree[rt].r ) >> 1;
if ( pos <= mid ) {
update ( lson, pos, x );
} else {
update ( rson, pos, x );
}
push_up ( rt );
}
int query ( int rt, int l, int r ) {
if ( segtree[rt].l == l && segtree[rt].r == r ) {
return segtree[rt].num;
}
int mid = ( segtree[rt].l + segtree[rt].r ) >> 1;
if ( r <= mid ) {
return query ( lson, l, r );
} else if ( l > mid ) {
return query ( rson, l, r );
} else {
return ( query ( lson, l, mid ) ^ query ( rson, mid + 1, r ) );
}
}
int main() {
scanf ( "%d%d", &n, &q );
for ( int i = 1; i <= n; ++i ) {
scanf ( "%d", &a[i] );
v.push_back ( a[i] );
}
build ( 1, 1, n );
for ( int i = 1; i <= q; ++i ) {
scanf ( "%d%d%d", &ask[i].op, &ask[i].l, &ask[i].r );
if ( ask[i].op == 1 ) {
v.push_back ( ask[i].r );
}
}
sort ( v.begin(), v.end() );
v.erase ( unique ( v.begin(), v.end() ), v.end() );
for ( int i = 1; i <= n; ++i ) {
a[i] = lower_bound ( v.begin(), v.end(), a[i] ) - v.begin() + 1;
G[a[i]].push_back ( i );
}
for ( int i = 1; i <= q; ++i ) {
if ( ask[i].op == 1 ) {
int pos = ask[i].l, val = ask[i].r;
update ( 1, pos, val );
int num = lower_bound ( v.begin(), v.end(), val ) - v.begin() + 1;
G[num].push_back ( pos );
sort ( G[num].begin(), G[num].end() );
it = lower_bound ( G[a[pos]].begin(), G[a[pos]].end(), pos );
if ( it == G[a[pos]].end() ) {
continue;
}
G[a[pos]].erase ( it );
a[pos] = num;
} else {
int l = ask[i].l, r = ask[i].r;
int num = query ( 1, l, r );
it = lower_bound ( v.begin(), v.end(), num );
if ( it == v.end() ) {
printf ( "%d\n", ask[i].r - ask[i].l + 1 );
} else if ( ( *it ) != num ) {
printf ( "%d\n", ask[i].r - ask[i].l + 1 );
} else {
int cnt = 0;
num = lower_bound(v.begin(), v.end(), num) - v.begin() + 1;
int pos1 = upper_bound ( G[num].begin(), G[num].end(), ask[i].r ) - G[num].begin();
int pos2 = lower_bound ( G[num].begin(), G[num].end(), ask[i].l ) - G[num].begin();
cnt = pos1 - pos2;
printf ( "%d\n", ask[i].r - ask[i].l + 1 - cnt );
}
}
}
return 0;
}
树套树写法
思路
假如数据并没有保证序列中相同的数字不会超过200个,那么用上述解法就无法解决,这个时候我们就可以使用树套树来解决。
我的解法是用一棵线段树维护区间异或值和上述解法一致,然后用树状数组\(+\)主席树来维护每个数对应的个数。
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> piL;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("in","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int n, q, tot1, tot2, cnt, tt;
int a[maxn*10], root1[maxn*10], root2[maxn*10];
int q1[maxn*10], q2[maxn*10], num[maxn*20];
struct pp {
int op, x, y;
}ask[maxn];
struct node {
int l, r, num;
}segtree[maxn*40], tree[maxn*500];
void push_up(int rt) {
segtree[rt].num = (segtree[lson].num ^ segtree[rson].num);
}
void build(int rt, int l, int r) {
segtree[rt].l = l, segtree[rt].r = r;
segtree[rt].num = 0;
if(l == r) {
segtree[rt].num = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid + 1, r);
push_up(rt);
}
void update(int rt, int x, int val) {
if(segtree[rt].l == segtree[rt].r) {
segtree[rt].num = val;
return;
}
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
if(x <= mid) update(lson, x, val);
else update(rson, x, val);
push_up(rt);
}
int query(int rt, int l, int r) {
if(segtree[rt].l == l && segtree[rt].r == r) {
return segtree[rt].num;
}
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
if(r <= mid) return query(lson, l, r);
else if(l > mid) return query(rson, l, r);
else return (query(lson, l, mid) ^ query(rson, mid + 1, r));
}
int getid(int x) {
return lower_bound(num, num + tt, x) - num + 1;
}
void update1(int l, int r, int& x, int y, int pos, int val) {
tree[++cnt] = tree[y], tree[cnt].num += val, x = cnt;
if(l == r) return;
int mid = (l + r) >> 1;
if(pos <= mid) update1(l, mid, tree[x].l, tree[y].l, pos, val);
else update1(mid + 1, r, tree[x].r, tree[y].r, pos, val);
}
int query1(int l, int r, int x, int y, int pos) {
if(l == r) {
int ans = tree[y].num - tree[x].num;
for(int i = 0; i < tot1; ++i) ans -= tree[q1[i]].num;
for(int i = 0; i < tot2; ++i) ans += tree[q2[i]].num;
return ans;
}
int mid = (l + r) >> 1;
if(pos <= mid) {
for(int i = 0; i < tot1; ++i) q1[i] = tree[q1[i]].l;
for(int i = 0; i < tot2; ++i) q2[i] = tree[q2[i]].l;
return query1(l, mid, tree[x].l, tree[y].l, pos);
}else {
for(int i = 0; i < tot1; ++i) q1[i] = tree[q1[i]].r;
for(int i = 0; i < tot2; ++i) q2[i] = tree[q2[i]].r;
return query1(mid + 1, r, tree[x].r, tree[y].r, pos);
}
}
int main() {
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
num[tt++] = a[i];
}
for(int i = 1; i <= q; ++i) {
scanf("%d%d%d", &ask[i].op, &ask[i].x, &ask[i].y);
if(ask[i].op == 1) num[tt++] = ask[i].y;
}
sort(num, num + tt);
tt = unique(num, num + tt) - num;
int sz = tt;
build(1, 1, n);
for(int i = 1; i <= n; ++i) {
a[i] = getid(a[i]);
update1(1, sz, root1[i], root1[i-1], a[i], 1);
}
for(int i = 1; i <= q; ++i) {
if(ask[i].op == 1) {
int pos = ask[i].x, val = ask[i].y, x = a[pos];
a[pos] = val;
update(1, pos, val);
val = getid(val);
while(pos <= n) {
update1(1, sz, root2[pos], root2[pos], x, -1);
update1(1, sz, root2[pos], root2[pos], val, 1);
pos += lowbit(pos);
}
} else {
int numm = query(1, ask[i].x, ask[i].y);
int pos = lower_bound(num, num + tt, numm) - num;
if(pos == tt || num[pos] != numm) {
printf("%d\n", ask[i].y - ask[i].x + 1);
continue;
}
numm = getid(numm);
tot1 = tot2 = 0;
pos = ask[i].x - 1;
while(pos) {
q1[tot1++] = root2[pos];
pos -= lowbit(pos);
}
pos = ask[i].y;
while(pos) {
q2[tot2++] = root2[pos];
pos -= lowbit(pos);
}
printf("%d\n", ask[i].y - ask[i].x + 1 - query1(1, sz, root1[ask[i].x-1], root1[ask[i].y], numm));
}
}
return 0;
}
CDQ分治写法
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> pil;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("D://code//in.txt","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1000000007;
const int maxn = 2e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int n, m, tot;
vector<int> v;
int a[maxn*5], tree[maxn*5], num[maxn*6], ans[maxn*6];
struct que {
int num, op, pos, type, id;
bool operator < (const que& x) const {
return pos == x.pos ? op < x.op : pos < x.pos;
}
}ask[maxn*10], tmp[maxn*10];
void bit_update(int x, int val) {
for(; x <= n; x += lowbit(x)) {
tree[x] ^= val;
}
}
int bit_query(int x) {
int ans = 0;
while(x > 0) {
ans ^= tree[x];
x -= lowbit(x);
}
return ans;
}
struct node {
int l, r, op, num;
}q[maxn];
int getid(int x) {
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
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;
for (int i = L; i <= R; i++) {
if ((p <= mid && ask[p] < ask[q]) || q > R) {
if (ask[p].op == 1) {
int x = ask[p].num;
num[x] += ask[p].type;
}
tmp[i] = ask[p++];
} else {
if (ask[q].op == 2) {
int x = ask[q].num;
ans[ask[q].id] += ask[q].type * num[x];
}
tmp[i] = ask[q++];
}
}
for (int i = L; i <= R; i++) {
if (i <= mid) {
if (ask[i].op == 1) {
int x = ask[i].num;
num[x] -= ask[i].type;
}
}
ask[i] = tmp[i];
}
}
int main(){
#ifndef ONLINE_JUDGE
FIN;
freopen("out.txt","w",stdout);
#endif
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
v.push_back(a[i]);
bit_update(i, a[i]);
}
for(int i = 1; i <= m; ++i) {
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
q[i].op = op, q[i].l = x, q[i].r = y;
if(op == 1) {
v.push_back(y);
int num = (bit_query(x-1) ^ bit_query(x));
bit_update(x, num);
bit_update(x, y);
} else {
q[i].num = (bit_query(x - 1) ^ bit_query(y));
v.push_back(q[i].num);
}
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
int cnt = 0;
for(int i = 1; i <= n; ++i) {
ask[++tot] = {getid(a[i]), 1, i, 1, 0};
}
for(int i = 1; i <= m; ++i) {
int op = q[i].op, x = q[i].l, y = q[i].r, num = q[i].num;
if(op == 1) {
ask[++tot] = {getid(a[x]), 1, x, -1, 0};
ask[++tot] = {getid(y), 1, x, 1, 0};
a[x] = y;
} else {
ask[++tot] = {getid(num), 2, x - 1, 1, ++cnt};
ask[++tot] = {getid(num), 2, y, -1, cnt};
ans[cnt] = y - x + 1;
}
}
CDQ(1, tot);
for(int i = 1; i <= cnt; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}
带修改莫队写法
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> piL;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("in","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 2e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int n, q, block, all;
int a[maxn];
unordered_map<int,int> cnt;
struct que {
int l, r, id, t, ans;
bool operator < (const que& x) const {
if((l - 1) / block != (x.l - 1) / block) {
return l < x.l;
}
if((r - 1) / block != (x.r - 1) / block) {
return r < x.r;
}
return t < x.t;
}
}ask[maxn];
struct modify {
int pre, val, pos;
}mfy[maxn];
void del(int x) {
cnt[x]--;
all ^= x;
}
void add(int x) {
cnt[x]++;
all ^= x;
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
freopen("out","w",stdout);
#endif
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
block = (int)pow(n, 2.0/3);
int nw = 0, idq = 0, idc = 1;
for (int i = 1; i <= q; i++) {
int op; scanf("%d", &op);
if (op == 2) {
scanf("%d%d", &ask[idq].l, &ask[idq].r);
ask[idq].id = idq;
ask[idq].t = nw;
idq++;
} else {
scanf("%d%d", &mfy[idc].pos, &mfy[idc].val); nw++;
mfy[idc].pre = a[mfy[idc].pos];
a[mfy[idc].pos] = mfy[idc].val;
idc++;
}
}
sort(ask, ask + idq);
int tmp = nw, r = 0, l = 1;
for (int i = 0; i < idq; i++) {
while (r < ask[i].r) add(a[++r]);
while (l > ask[i].l) add(a[--l]);
while (r > ask[i].r) del(a[r--]);
while (l < ask[i].l) del(a[l++]);
while (tmp < ask[i].t) {
tmp++;
if (mfy[tmp].pos >= ask[i].l && mfy[tmp].pos <= ask[i].r) {
del(mfy[tmp].pre);
add(mfy[tmp].val);
}
a[mfy[tmp].pos] = mfy[tmp].val;
}
while (tmp > ask[i].t) {
if (mfy[tmp].pos >= ask[i].l && mfy[tmp].pos <= ask[i].r) {
del(mfy[tmp].val);
add(mfy[tmp].pre);
}
a[mfy[tmp].pos] = mfy[tmp].pre;
tmp--;
}
int res = cnt[all];
ask[ask[i].id].ans = (r - l + 1) - res;
}
for(int i = 0; i < idq; ++i) {
printf("%d\n", ask[i].ans);
}
return 0;
}
Tulip Festival(线段树+二分+CDQ+带修改莫队+树套树)的更多相关文章
- BZOJ2120&2453数颜色——线段树套平衡树(treap)+set/带修改莫队
题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2 ...
- 【BZOJ】4129: Haruna’s Breakfast 树分块+带修改莫队算法
[题意]给定n个节点的树,每个节点有一个数字ai,m次操作:修改一个节点的数字,或询问一条树链的数字集合的mex值.n,m<=5*10^4,0<=ai<=10^9. [算法]树分块+ ...
- 【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法
[题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的 ...
- [BZOJ4129]Haruna’s Breakfast(树上带修改莫队)
BZOJ3585,BZOJ2120,BZOJ3757三合一. 对于树上路径问题,树链剖分难以处理的时候,就用树上带修改莫队. 这里的MEX问题,使用BZOJ3585的分块方法,平衡了时间复杂度. 剩下 ...
- 莫队 [洛谷2709] 小B的询问[洛谷1903]【模板】分块/带修改莫队(数颜色)
莫队--------一个优雅的暴力 莫队是一个可以在O(n√n)内求出绝大部分无修改的离线的区间问题的答案(只要问题满足转移是O(1)的)即你已知区间[l,r]的解,能在O(1)的时间内求出[l-1, ...
- BZOJ2120 数颜色(带修改莫队)
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
- bzoj 2120 数颜色 带修改莫队
带修改莫队,每次查询前调整修改 #include<cstdio> #include<iostream> #include<cstring> #include< ...
- BZOJ.2453.维护队列([模板]带修改莫队)
题目链接 带修改莫队: 普通莫队的扩展,依旧从[l,r,t]怎么转移到[l+1,r,t],[l,r+1,t],[l,r,t+1]去考虑 对于当前所在的区间维护一个vis[l~r]=1,在修改值时根据是 ...
- BZOJ.3052.[WC2013]糖果公园(树上莫队 带修改莫队)
题目链接 BZOJ 当然哪都能交(都比在BZOJ交好),比如UOJ #58 //67376kb 27280ms //树上莫队+带修改莫队 模板题 #include <cmath> #inc ...
随机推荐
- [数据结构 - 第6章] 树之二叉平衡树(C语言实现)
一.什么是平衡二叉树? 平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两 ...
- mysql 导入txt数据到数据表【原创】
1.如何将数据表导入到mysql的表中,可以使用:load data infile ... into table ... 示例: load data infile 'e:\datainfo.txt' ...
- lvm的一些特殊命令
pvscan --cache # 将lvm信息同步到其他节点 ......未完待续
- vba实现工具的序列号验证框架
对于密码破译方面笔者不太懂,之前对于各种序列号的激活也有些臆测,自己根据想法做了个序列号验证的小框架,以后做的工具也可以用之保护一下下... 主要思路是:用户打开小工具后,系统检测是否已激活,如果未激 ...
- JSON学习(二)
首先,定义一个实体类Person: import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; public ...
- NAIPC 2018
E. Prefix Free Code 大意: 给定$n$个串, 保证任意一个串都不是另一个串的前缀, 从中选出$k$个串可以拼成$\binom{n}{k}k!$种串. 给定其中一个串, 求这个串的排 ...
- Maven聚合项目的创建
1.项目结构如下 步骤如下: 点击Finish 这里父项目需要加入如下的构建依赖: <dependencyManagement> <dependencies> <depe ...
- in __init__ self._traceback = tf_stack.extract_stack()的一个原因
这样就会出错,原因在于函数返回为三个变量,若直接向写入函数那样运用sess.run()得到,就会导致错误. sess=tf.Session() sess.run(tf.initialize_all_v ...
- JQuey中ready()的4种写法
在jQuery中,对于ready()方法,共有4种写法: (1)写法一: $(document).ready(functin(){ //代码部分 }) 分析:这种代码形式是最常见的,其中$(docum ...
- Flask框架 请求与响应 & 模板语法
目录 Flask框架 请求与响应 & 模板语法 简单了解Flask框架 Flask 框架 与 Django 框架对比 简单使用Flask提供服务 Flask 中的 Response(响应) F ...