每个房间用一个集合来维护,具体来说,就是给1-n的数每个数一个long long的hash值,往集合S里insert(i),就是S^=HASH[i];erase(i),也是S^=HASH[i]。
用map/set维护某个集合是否已经做过实验。
分块,对每个块维护一个maxv[i],代表当前该块内的答案值,要注意,若某个房间的集合的vis[S[i]]==true,则它对其所在块没有贡献。
分块相对于线段树/平衡树/树套树的很大的好处,就是思路简单暴力,代码短,常数小,应对范围广,可以方便地扩张。
像这种区间修改的问题,可以类比线段树打懒标记的思路来进行,使得每次修改是O(sqrt(n))的。
Pushdown函数需要每次对块进行“零散地”操作的之前进行。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<ctime> 6 #include<map> 7 using namespace std; 8 unsigned long long HASH[100001]; 9 struct SET 10 { 11 unsigned long long S;int size; 12 void insert(const int &v){S^=HASH[v];size++;} 13 void erase(const int &v){S^=HASH[v];size--;} 14 }; 15 int n,m,q,l[600],r[600],sumv[600],sum,sz,num[100001],x,y,pos[100001]; 16 bool delta[600]; 17 SET sets[100001]; 18 map<unsigned long long,bool>vis; 19 char op[1]; 20 int Res,Num;char C,CH[12]; 21 inline int G() 22 { 23 Res=0;C=‘*‘; 24 while(C<‘0‘||C>‘9‘)C=getchar(); 25 while(C>=‘0‘&&C<=‘9‘){Res=Res*10+(C-‘0‘);C=getchar();} 26 return Res; 27 } 28 inline void P(long long x) 29 { 30 Num=0;if(!x){putchar(‘0‘);puts("");return;} 31 while(x>0)CH[++Num]=x%10,x/=10; 32 while(Num)putchar(CH[Num--]+48); 33 puts(""); 34 } 35 void makeblock() 36 { 37 srand(233); 38 for(int i=1;i<=n;i++) HASH[i]=(unsigned long long)((unsigned long long)rand()<<48)|((unsigned long long)rand()<<32)|((unsigned long long)rand()<<16)|((unsigned long long)rand()); 39 sz=sqrt((double)m*0.3); 40 for(int i=1;i<=n;i++) {sets[1].insert(i); pos[i]=1;} 41 sumv[1]=n; 42 for(sum=1;sum*sz<m;sum++) 43 { 44 l[sum]=(sum-1)*sz+1; 45 r[sum]=sum*sz; 46 for(int i=l[sum];i<=r[sum];i++) num[i]=sum; 47 } 48 l[sum]=sz*(sum-1)+1; r[sum]=m; 49 for(int i=l[sum];i<=r[sum];i++) num[i]=sum; 50 } 51 inline void Pushdown(const int &p)//对块p下传标记,Pushdown操作需要每次对块进行零散操作之前进行 52 { 53 if(delta[p]) 54 { 55 for(int i=l[p];i<=r[p];i++) 56 if(sets[i].size) 57 vis[sets[i].S]=true; 58 delta[p]=false; 59 } 60 } 61 inline void Move() 62 { 63 Pushdown(num[pos[x]]); 64 bool flag1=vis[sets[pos[x]].S]; 65 sets[pos[x]].erase(x); 66 bool flag2=vis[sets[pos[x]].S]; 67 if(flag1&&!flag2) sumv[num[pos[x]]]+=sets[pos[x]].size; 68 else if(!flag1&&flag2) sumv[num[pos[x]]]-=(sets[pos[x]].size+1); 69 else if(!flag1&&!flag2) sumv[num[pos[x]]]--; 70 Pushdown(num[y]); 71 flag1=vis[sets[y].S]; 72 sets[y].insert(x); 73 flag2=vis[sets[y].S]; 74 if(flag1&&!flag2) sumv[num[y]]+=sets[y].size; 75 else if(!flag1&&flag2) sumv[num[y]]-=(sets[y].size-1); 76 else if(!flag1&&!flag2) sumv[num[y]]++; 77 pos[x]=y; 78 } 79 inline void Update_Query() 80 { 81 int ans=0; 82 Pushdown(num[x]); 83 Pushdown(num[y]); 84 if(num[x]+1>=num[y]) 85 { 86 for(int i=x;i<=y;i++) 87 if(sets[i].size) 88 if(!vis[sets[i].S]) 89 { 90 ans+=sets[i].size; 91 sumv[num[i]]-=sets[i].size; 92 vis[sets[i].S]=true; 93 } 94 } 95 else 96 { 97 for(int i=x;i<=r[num[x]];i++) 98 if(sets[i].size) 99 if(!vis[sets[i].S]) 100 { 101 ans+=sets[i].size; 102 sumv[num[x]]-=sets[i].size; 103 vis[sets[i].S]=true; 104 } 105 for(int i=l[num[y]];i<=y;i++) 106 if(sets[i].size) 107 if(!vis[sets[i].S]) 108 { 109 ans+=sets[i].size; 110 sumv[num[y]]-=sets[i].size; 111 vis[sets[i].S]=true; 112 } 113 for(int i=num[x]+1;i<num[y];i++) 114 { 115 if(!delta[i]) 116 ans+=sumv[i]; 117 delta[i]=true; 118 sumv[i]=0; 119 } 120 } 121 P(ans); 122 } 123 int main() 124 { 125 n=G();m=G();q=G(); 126 makeblock(); 127 for(;q>0;q--) 128 { 129 scanf("%s",op);x=G();y=G(); 130 if(op[0]==‘C‘) Move(); 131 else Update_Query(); 132 } 133 return 0; 134 }
时间: 2024-11-09 10:48:48