HDU5511 : Minimum Cut-Cut
設$d[x]$表示端點位于$x$子樹內部的非樹邊條數,那么有兩種情況:
$1.$割去的兩條樹邊$(x,fa[x]),(y,fa[y])$中,$x$是$y$的祖先,那么此時需要割去的非樹邊數量為$d[x]-d[y]$。
顯然固定$x$之后$y$越靠上越好,因此$y$一定是$x$的兒子,枚舉即可,時間復雜度$O(n)$。
$2.$一般化的情況,此時$x\neq y$,需要割去的非樹邊數量為$d[x]+d[y]-2cnt(x,y)$,其中$cnt(x,y)$表示一端在$x$子樹中,另一端在$y$子樹中的非樹邊數量。
枚舉$x$,對于每個$y$維護$d[y]-2cnt(x,y)$。
首先需要把$x$子樹的$cnt$合并,然后對于一條端點恰好為$x$的非樹邊$(x,u)$,需要把$u$到根路徑上的$d-2cnt$都減去$2$。
樹鏈剖分+線段樹維護即可,時間復雜度$O(m\log^2n)$。
直接實現的話,空間復雜度也為$O(m\log^2n)$,不能承受。
每次先dfs重兒子,將$x$的重兒子的線段樹直接作為$x$的線段樹,然后依次與輕兒子合并,同時將廢棄線段樹節點回收利用。
如此一來,只有經過輕邊時,才會多開一棵動態開點的線段樹,那么一共只會同時存在$O(\log n)$棵線段樹。
每棵線段樹就算開滿也只有$O(n)$的空間,因此空間復雜度為$O(n\log n)$。
?
#include<cstdio> const int N=20010,M=100010,E=1200000; int Case,cas,n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed,ans; int d[N],G[N],V[M<<1],NXT[M<<1],ED; int f[N],size[N],son[N],top[N],loc[N],dfn,q[N]; int T[N],l[E],r[E],tag[E],val[E],pool[E],cur,w[66000]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void ADD(int x,int y){d[x]++;V[++ED]=y;NXT[ED]=G[x];G[x]=ED;} inline void umin(int&a,int b){a>b?(a=b):0;} inline int min(int a,int b){return a<b?a:b;} void dfs(int x){size[x]=1;for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){f[v[i]]=x;dfs(v[i]);size[x]+=size[v[i]],d[x]+=d[v[i]];if(size[v[i]]>size[son[x]])son[x]=v[i];}if(x>1)for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x])umin(ans,d[x]-d[v[i]]); } void dfs2(int x,int y){loc[x]=++dfn;top[x]=y;if(son[x])dfs2(son[x],y);for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]); } void build(int x,int a,int b){if(a==b){w[x]=q[a];return;}int mid=(a+b)>>1;build(x<<1,a,mid),build(x<<1|1,mid+1,b);w[x]=min(w[x<<1],w[x<<1|1]); } inline int newnode(int o){int x=pool[cur--];l[x]=r[x]=tag[x]=0;val[x]=w[o];return x; } inline void tag1(int x,int p){tag[x]+=p;val[x]+=p;} inline void pb(int x,int o){if(tag[x]){if(!l[x])l[x]=newnode(o<<1);if(!r[x])r[x]=newnode(o<<1|1);tag1(l[x],tag[x]),tag1(r[x],tag[x]),tag[x]=0;} } inline void up(int x,int o){if(!l[x])l[x]=newnode(o<<1);if(!r[x])r[x]=newnode(o<<1|1);val[x]=min(val[l[x]],val[r[x]]); } void change(int&x,int o,int a,int b,int c,int d,int p){if(!x)x=newnode(o);if(c<=a&&b<=d){tag1(x,p);return;}pb(x,o);int mid=(a+b)>>1;if(c<=mid)change(l[x],o<<1,a,mid,c,d,p);if(d>mid)change(r[x],o<<1|1,mid+1,b,c,d,p);up(x,o); } inline void chain(int&T,int x){while(top[x]!=1)change(T,1,1,n,loc[top[x]],loc[x],-2),x=f[top[x]];if(x>1)change(T,1,1,n,2,loc[x],-2); } int merge(int x,int y,int o,int a,int b){if(!x||!y)return x+y;if(a==b)tag[x]+=tag[y];else{pb(x,o),pb(y,o);int mid=(a+b)>>1;l[x]=merge(l[x],l[y],o<<1,a,mid);r[x]=merge(r[x],r[y],o<<1|1,mid+1,b);up(x,o);}pool[++cur]=y;return x; } void clear(int x,int a,int b){if(!x)return;pool[++cur]=x;if(a==b)return;int mid=(a+b)>>1;clear(l[x],a,mid),clear(r[x],mid+1,b); } void dfs3(int x){if(son[x])dfs3(son[x]),T[x]=T[son[x]];for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs3(v[i]),T[x]=merge(T[x],T[v[i]],1,1,n);if(x==1)return;for(int i=G[x];i;i=NXT[i])chain(T[x],V[i]);change(T[x],1,1,n,loc[x],loc[x],M);umin(ans,d[x]+val[T[x]]);change(T[x],1,1,n,loc[x],loc[x],-M); } int main(){for(i=1;i<E;i++)pool[++cur]=i;scanf("%d",&Case);for(cas=1;cas<=Case;cas++){scanf("%d%d",&n,&m);ans=M;for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);for(;i<=m;i++)scanf("%d%d",&x,&y),ADD(x,y),ADD(y,x);dfs(1),dfs2(1,1);for(i=1;i<=n;i++)q[loc[i]]=d[i];build(1,1,n);dfs3(1);printf("Case #%d: %d\n",cas,ans+2);clear(T[1],1,n);for(ed=ED=dfn=0,i=1;i<=n;i++)g[i]=d[i]=G[i]=f[i]=size[i]=son[i]=T[i]=0;}return 0; }與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的HDU5511 : Minimum Cut-Cut的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c#的逆变和协变
- 下一篇: 基于 Quartz 开发企业级任务调度应