这题说白了就是一个支持加边和删边的图连通性维护,不过鉴于图的特殊性,可以直接线段树(听说标算就是这个……)。
然而我人比较懒,不想思考怎么线段树,于是乎写了一发分治并查集,1A我真是感动……
话说一时没想起来怎么写能撤销的按秩合并,于是乎写了个随机合并(反正期望复杂度都是logn的),代码开头那个玩意儿就是手写的随机数生成器……(我会说模数取998244353之后即使int有溢出也能保证循环节在一千万以上嘛……)
第一发分治并查集,又学会了一个新技能哈哈……
话说这份代码在bzoj上被卡了,真是忧伤……
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<map> 6 using namespace std; 7 const int maxn=100010; 8 inline int randint(){ 9 static int a=151,b=24863,c=9788417,x=1089488183,p=998244353; 10 x=a*x*x+b*x+c;x%=p; 11 return x<0?(x=-x):x; 12 } 13 struct A{int u,v,tp;}a[maxn]; 14 void addedge(int,int,int); 15 void addquery(int,int,int); 16 void solve(int,int,int); 17 int findroot(int); 18 void mergeset(int,int,vector<int>&); 19 int id[3][maxn],cnt=0,prt[maxn<<1],qu[maxn<<2],qv[maxn<<2]; 20 int n,m=1,x1,y1,x2,y2,x,y,s,t; 21 vector<int>u[maxn<<2],v[maxn<<2],stk[maxn<<2]; 22 map<pair<int,int>,int>mp; 23 char c[15]; 24 int main(){ 25 freopen("bzoj_1018.in","r",stdin); 26 freopen("bzoj_1018.out","w",stdout); 27 scanf("%d",&n); 28 for(int i=1;i<=n;i++){ 29 id[1][i]=++cnt; 30 id[2][i]=++cnt; 31 } 32 for(int i=1;i<=cnt;i++)prt[i]=i; 33 for(int &i=m;scanf("%s",c)==1&&strcmp(c,"Exit");i++){ 34 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 35 x=id[x1][y1];y=id[x2][y2]; 36 if(x>y)swap(x,y); 37 a[i].u=x;a[i].v=y; 38 if(!strcmp(c,"Open"))a[i].tp=1; 39 else if(!strcmp(c,"Close"))a[i].tp=2; 40 else a[i].tp=3; 41 } 42 for(int i=1;i<=m;i++){ 43 x=a[i].u;y=a[i].v; 44 if(a[i].tp==1){ 45 if(!mp.count(make_pair(x,y)))mp[make_pair(x,y)]=i; 46 } 47 else if(a[i].tp==2){ 48 if(mp.count(make_pair(x,y))){ 49 s=mp[make_pair(x,y)]; 50 t=i; 51 addedge(1,m,1); 52 mp.erase(make_pair(x,y)); 53 } 54 } 55 else{ 56 t=i; 57 addquery(1,m,1); 58 } 59 } 60 for(map<pair<int,int>,int>::iterator it=mp.begin();it!=mp.end();it++){ 61 x=it->first.first; 62 y=it->first.second; 63 s=it->second; 64 t=m; 65 addedge(1,m,1); 66 } 67 solve(1,m,1); 68 return 0; 69 } 70 void addedge(int l,int r,int rt){ 71 if(s<=l&&t>=r){ 72 u[rt].push_back(x); 73 v[rt].push_back(y); 74 return; 75 } 76 int mid=(l+r)>>1; 77 if(s<=mid)addedge(l,mid,rt<<1); 78 if(t>mid)addedge(mid+1,r,rt<<1|1); 79 } 80 void addquery(int l,int r,int rt){ 81 if(l==r){ 82 qu[rt]=x; 83 qv[rt]=y; 84 return; 85 } 86 qu[rt]|=x; 87 int mid=(l+r)>>1; 88 if(t<=mid)addquery(l,mid,rt<<1); 89 else addquery(mid+1,r,rt<<1|1); 90 } 91 void solve(int l,int r,int rt){ 92 if(!qu[rt])return; 93 for(int i=0;i<(int)u[rt].size();i++)mergeset(u[rt][i],v[rt][i],stk[rt]); 94 if(l==r)printf(findroot(qu[rt])==findroot(qv[rt])?"Y ":"N "); 95 else{ 96 int mid=(l+r)>>1; 97 solve(l,mid,rt<<1); 98 solve(mid+1,r,rt<<1|1); 99 } 100 if(!stk[rt].empty())for(int i=(int)stk[rt].size()-1;i>=0;i--)prt[stk[rt][i]]=stk[rt][i]; 101 } 102 int findroot(int x){ 103 while(prt[x]!=x)x=prt[x]; 104 return x; 105 } 106 void mergeset(int x,int y,vector<int>&a){ 107 x=findroot(x);y=findroot(y); 108 if(x==y)return; 109 if(randint()&1)swap(x,y); 110 prt[x]=y; 111 a.push_back(x); 112 }
不知道该加什么分类了,反正是对时间建线段树,加到CDQ分治里算了……