bzoj2120 数颜色
这篇题解拖了好久......原因是一直不知道该怎么叙述。
首先,类似采花那个题,统计答案的时候要找到每个位置之前和它颜色相同的第一个位置pre[i],那么只要这个位置的pre[i]不在查询的[L,R]区间里,就说明这个颜色是这个区间里第一次出现的颜色,答案+1。动态修改的话我们可以用set来实现。
然后传统的树套树做法是将序列建一棵线段树,再给线段树每个节点建一棵平衡树,代表[l,r]这段区间。那么每次查询[L,R]的时候在线段树上走一下,找到相应位置,查找比询问L小的有多少个就行了。
但是有一种更为简洁的做法——整体二分!
以前都是用整体二分去搞一些第k小数问题,但这个题其实和第k小数有很大的联系:找区间k小数等价于找到一个数a使得区间内比它大的数有k-1个(问题1);而此题等价于找到一个k使得小于等于数a的数恰好有k个(问题2)。这似乎是非常对偶的两个问题,那么怎么用整体二分呢?我们回想找第k小数的过程,我们二分了一个值M,然后去找小于等于M的数有多少个,根据这个来划分询问。也就是说,在求问题1的时候我们一定要求出问题2的答案,那么我们不就可以利用这个过程直接把问题2求出来了吗?那么我们在判断询问的归属的时候,直接利用问题2的答案来判定,也就是根据当前答案+贡献值和询问左端点的大小比较。这样最后在每个询问上累计的贡献就是最终这个询问的答案。
我们再考虑一下整体二分的意义,实际上我们二分的就是问题2里的那个a,也就是我们维护的pre,这相当于在一棵权值线段树里面寻找答案。对值域的整体二分就相当于离线建立的一棵隐式的权值线段树,但我们用权值线段树查询的时候是单路求值,用整体二分的话就是多路求值,是把所有的询问套在一起做,一起出答案 。
color
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #include<set> 7 #define maxn 24000 8 #define high 1000002 9 using namespace std; 10 struct query 11 { 12 int x,y,cur,tp,s; 13 }q[maxn],q1[maxn],q2[maxn]; 14 int a[maxn],ans[maxn],tmp[maxn]; 15 int n,m,num,cnt; 16 struct bit 17 { 18 int b[maxn]; 19 void add(int x,int z) 20 { 21 for (int i=x;i<=n;i+=(i&-i)) b[i]+=z; 22 } 23 int ask(int x) 24 { 25 int tmp=0; 26 for (int i=x;i>0;i-=(i&-i)) tmp+=b[i]; 27 return tmp; 28 } 29 }t; 30 set<int> s[high]; 31 set<int>::iterator p; 32 //set是颜色库,用来存储同种颜色的位置 33 34 void divide(int head,int tail,int l,int r) 35 { 36 if (head>tail) return; 37 if (l==r) 38 { 39 for (int i=head;i<=tail;i++) 40 if (q[i].tp==3) ans[q[i].s]=q[i].cur; 41 return ; 42 } 43 int mid=(l+r)>>1; 44 for (int i=head;i<=tail;i++) 45 { 46 if (q[i].tp==1&&q[i].y<=mid) t.add(q[i].x,1); 47 else 48 if (q[i].tp==2&&q[i].y<=mid) t.add(q[i].x,-1); 49 else 50 if (q[i].tp==3) tmp[i]=t.ask(q[i].y)-t.ask(q[i].x-1); 51 } 52 for (int i=head;i<=tail;i++) 53 { 54 if (q[i].tp==1&&q[i].y<=mid) t.add(q[i].x,-1); 55 else 56 if (q[i].tp==2&&q[i].y<=mid) t.add(q[i].x,1); 57 } 58 int l1=0,l2=0; 59 for (int i=head;i<=tail;i++) 60 { 61 if (q[i].tp==3) 62 if (q[i].x<=mid) q1[++l1]=q[i]; 63 else 64 { 65 q[i].cur+=tmp[i]; 66 q2[++l2]=q[i]; 67 } 68 else 69 if (q[i].y<=mid) q1[++l1]=q[i]; 70 else q2[++l2]=q[i]; 71 } 72 for (int i=1;i<=l1;i++) q[head+i-1]=q1[i]; 73 for (int i=1;i<=l2;i++) q[head+l1+i-1]=q2[i]; 74 divide(head,head+l1-1,l,mid); 75 divide(tail-l2+1,tail,mid+1,r); 76 } 77 78 int prev(int x,int c) 79 { 80 p=s[c].lower_bound(x); 81 if (p!=s[c].begin()) return *(--p); 82 else return 0; 83 } 84 int succ(int x,int c) 85 { 86 p=s[c].upper_bound(x); 87 if (p!=s[c].end()) return *p; 88 else return 0; 89 } 90 91 int main() 92 { 93 scanf("%d%d",&n,&m); 94 for (int i=1;i<=n;i++) 95 { 96 scanf("%d",&a[i]); 97 s[a[i]].insert(i); 98 int l=prev(i,a[i]); 99 q[++num].tp=1; 100 q[num].x=i; q[num].y=l; 101 } 102 char sign; 103 int x,y; 104 for (int i=1;i<=m;i++) 105 { 106 scanf("\n%c%d%d",&sign,&x,&y); 107 if (sign=='Q') 108 { 109 q[++num].tp=3; 110 q[num].x=x; q[num].y=y; 111 q[num].s=++cnt; 112 } 113 else 114 { 115 int l1=prev(x,a[x]),l2=prev(x,y); 116 int r1=succ(x,a[x]),r2=succ(x,y); 117 if (r1) 118 { 119 q[++num].tp=2; q[num].x=r1; q[num].y=x; 120 q[++num].tp=1; q[num].x=r1; q[num].y=l1; 121 } 122 q[++num].tp=2; q[num].x=x; q[num].y=l1; 123 q[++num].tp=1; q[num].x=x; q[num].y=l2; 124 if (r2) 125 { 126 q[++num].tp=2; q[num].x=r2; q[num].y=l2; 127 q[++num].tp=1; q[num].x=r2; q[num].y=x; 128 } 129 s[a[x]].erase(x); 130 a[x]=y; 131 s[a[x]].insert(x); 132 } 133 } 134 divide(1,num,0,high); 135 for (int i=1;i<=cnt;i++) printf("%d\n",ans[i]); 136 return 0; 137 } 138 139 140
感谢Martin大神提供思路orz。
AC without art, no better than WA !