题面写的是个什么玩意儿……
大概就是说,你有 $m$ 个集合,$n$ 个元素。起初所有元素都在 $1$ 号集合。
有两种操作:一种是给元素换集合;一种是查询一个子区间内没有被标记过的集合的元素个数和,查询完毕后把这个子区间的集合都标记。(注意:集合有没有被标记不在于集合的编号,而在于这个集合内的元素)
怎么做呢?给每个元素 rand 一个权值,集合的权值是元素的权值的异或。map 记录(集合权值)一个集合是否被标记过,set 记录(编号)哪些集合对答案有贡献,查询时 lower_bound 就行了。
谜之复杂度QAQ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <iostream> #include <cstdlib> #include <cstdio> #include <ctime> #include <set> #include <map> using namespace std; int n, m, q, sz[100005], v[100005], x[100005], uu, vv, l[100005]; char ss[15]; set<int> se; map<int,int> mp; int rnd(){ return (long long)rand()*rand()%1000000000+1; } int main(){ srand(time(NULL)); cin>>n>>m>>q; sz[1] = n; for(int i=1; i<=n; i++){ x[i] = rnd(); v[1] ^= x[i]; l[i] = 1; } mp[0] = 1; se.insert(1); while(q--){ scanf("%s %d %d", ss, &uu, &vv); if(ss[0]=='C'){ sz[l[uu]]--; v[l[uu]] ^= x[uu]; if(!mp[v[l[uu]]]) se.insert(l[uu]); else se.erase(l[uu]); l[uu] = vv; sz[l[uu]]++; v[l[uu]] ^= x[uu]; if(!mp[v[l[uu]]]) se.insert(l[uu]); else se.erase(l[uu]); } else{ int ans=0; for(set<int>::iterator it=se.lower_bound(uu); *it<=vv && it!=se.end(); ){ mp[v[*it]] = 1; ans += sz[*it]; se.erase(it++); } printf("%d\n", ans); } } return 0; }
|