CF487E Tourists[圆方树+树剖(线段树套set)]
做这题的时候有点怂。。基本已经想到正解了。。结果感觉做法有点假,还是看了正解题解。。
首先提到简单路径上经过的点,就想到了一个关于点双的结论:两点间简单路径上所有可能经过的点的并等于路径上所有点所在点双的并,也就是说,在建一棵圆方树,方点表示所在点双里的最小点权,两个圆点之间的路径上所有方点的最小值就是答案。
然后这题有一个修改单点。。修改一个圆点的点权的时候和他相邻的方点维护的min都可能变,所以每个方点开一个multiset维护点双最小值就行了。
但是这样复杂度不能保证,因为每次圆点可能会和一堆方点相连,菊花图的时候就是单次$O(n\log n)$了。。
所以要考虑减少没用的修改。考虑到圆方树是一棵树(废话),对于一个圆点,修改的话,会影响到他的父亲方点和儿子方点。
但是,儿子方点影响不大,如果所有儿子方点的multiset里都不维护这个圆点,每次要查询儿子方点的min只需要和这个圆点合并取min就行了。
所以,每次只要把自己修改一下,把父亲方点对应的multiset修改一下,在树剖的线段树上传一下就行了。
所以,multiset只维护所有儿子圆点。可以结合下图。
如果修改紫色点,那么,对于3路径,lca是一个儿子方点,将其和紫色圆点合并min。。对于2路径,直接跨过紫色圆点了就不管了,对于1号路径,因为所在点双min可能会改掉,所以必须把父亲方点multiset改掉。
然后就随便打个树剖就行了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<set> 6 #include<cmath> 7 #include<queue> 8 #define mst(x) memset(x,0,sizeof x) 9 #define dbg(x) cerr << #x << " = " << x <<endl 10 #define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl 11 using namespace std; 12 typedef long long ll; 13 typedef double db; 14 typedef pair<int,int> pii; 15 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 16 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 17 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 18 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 19 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 20 template<typename T>inline T read(T&x){ 21 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 22 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 23 } 24 const int N=2e5+7,INF=0x3f3f3f3f; 25 struct thxorz{ 26 int head[N],nxt[N<<1],to[N<<1],tot; 27 inline void add(int x,int y){ 28 to[++tot]=y,nxt[tot]=head[x],head[x]=tot; 29 to[++tot]=x,nxt[tot]=head[y],head[y]=tot; 30 } 31 }G1,G2; 32 char opt[3]; 33 int A[N]; 34 int n,m,Q; 35 #define y G1.to[j] 36 int dfn[N],low[N],stk[N],top,tim2,cnt; 37 void tarjan(int x){ 38 dfn[x]=low[x]=++tim2,stk[++top]=x; 39 for(register int j=G1.head[x];j;j=G1.nxt[j]){ 40 if(!dfn[y]){ 41 tarjan(y),MIN(low[x],low[y]); 42 if(low[y]==dfn[x]){ 43 int tmp;++cnt; 44 do tmp=stk[top--],G2.add(cnt,tmp);while(tmp^y); 45 G2.add(cnt,x); 46 } 47 } 48 else MIN(low[x],dfn[y]); 49 } 50 } 51 #undef y 52 multiset<int> s[N]; 53 int fa[N],topfa[N],son[N],sum[N],dep[N],pos[N],id[N],tim; 54 #define y G2.to[j] 55 void dfs1(int x,int fat){//dbg(x); 56 fa[x]=fat,dep[x]=dep[fat]+1,sum[x]=1;int tmp=-1; 57 for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fat)dfs1(y,x),sum[x]+=sum[y],MAX(tmp,sum[y])&&(son[x]=y); 58 } 59 void dfs2(int x,int topf){ 60 topfa[x]=topf,pos[x]=++tim,id[tim]=x; 61 if(!son[x])return; 62 dfs2(son[x],topf); 63 if(x>n){ 64 s[x].insert(A[son[x]]); 65 for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y),s[x].insert(A[y]); 66 } 67 else for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y); 68 } 69 #undef y 70 struct SGT{ 71 int minv[N<<2]; 72 #define lc i<<1 73 #define rc i<<1|1 74 inline void pushup(int i){minv[i]=_min(minv[lc],minv[rc]);} 75 void build(int i,int L,int R){ 76 if(L==R){//dbg2(i,id[L]),dbg2(L,R); 77 if(id[L]>n)minv[i]=*s[id[L]].begin(); 78 else minv[i]=A[id[L]];//dbg(minv[i]); 79 return; 80 } 81 int mid=L+R>>1; 82 build(lc,L,mid),build(rc,mid+1,R);pushup(i); 83 } 84 int query_min(int i,int L,int R,int ql,int qr){ 85 if(ql<=L&&qr>=R)return minv[i]; 86 int mid=L+R>>1,ret=INF; 87 if(ql<=mid)MIN(ret,query_min(lc,L,mid,ql,qr)); 88 if(qr>mid)MIN(ret,query_min(rc,mid+1,R,ql,qr)); 89 return ret; 90 } 91 void update(int i,int L,int R,int x,int preval,int val){ 92 if(L==R){ 93 if(id[L]>n)s[id[L]].erase(s[id[L]].find(preval)),s[id[L]].insert(val),minv[i]=*s[id[L]].begin(); 94 else minv[i]=A[id[L]]=val; 95 return; 96 } 97 int mid=L+R>>1; 98 if(x<=mid)update(lc,L,mid,x,preval,val); 99 else update(rc,mid+1,R,x,preval,val); 100 pushup(i); 101 } 102 }T; 103 inline int ask(int x,int y){ 104 int ret=INF; 105 while(topfa[x]^topfa[y]){ 106 if(dep[topfa[x]]<dep[topfa[y]])_swap(x,y); 107 MIN(ret,T.query_min(1,1,cnt,pos[topfa[x]],pos[x])),x=fa[topfa[x]]; 108 } 109 if(dep[x]>dep[y])_swap(x,y); 110 MIN(ret,T.query_min(1,1,cnt,pos[x],pos[y])); 111 if(x>n)MIN(ret,A[fa[x]]); 112 return ret; 113 } 114 inline void change(int x,int val){ 115 if(fa[x])T.update(1,1,cnt,pos[fa[x]],A[x],val); 116 T.update(1,1,cnt,pos[x],0,val); 117 } 118 119 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 120 cnt=read(n),read(m),read(Q); 121 for(register int i=1;i<=n;++i)read(A[i]); 122 for(register int i=1,x,y;i<=m;++i)read(x),read(y),G1.add(x,y); 123 for(register int i=1;i<=n;++i)if(!dfn[i])top=0,tarjan(i); 124 dfs1(1,0),dfs2(1,1),T.build(1,1,cnt); 125 for(register int i=1,x,val,y;i<=Q;++i){ 126 scanf("%s",opt); 127 if(opt[0]=='A')read(x),read(y),printf("%d\n",ask(x,y)); 128 else read(x),read(val),change(x,val); 129 } 130 return 0; 131 }
总结:这题主要是以减少修改,增加一些合并操作为目标,同时注意要把圆方树是树的性质给用好(就是父亲和儿子的讨论什么的)。。