题解:
好像挺简单的
首先普通的圆方树处理的是仙人掌上的问题
构造圆方树的方法就是对于每个环 建立一个方点
然后原先不在环上的边,就用圆点和圆点相连
对于在环上的边,将点连向方点
然后我们来证明一个这是一棵树
1.首先这张图联通是比较显然的
2.方点数=原先环数=e-v+1
当前点数=方点+v=e+1=当前边数+1
显然满足这两条的肯定是树了
1.仙人掌最大独立集
我们知道基环树的最大独立集就是对环上每个点的树都跑一遍
再在环上跑一遍
树上类似,我们利用dfs序,将不在环上的边按照普通的树形dp处理
对于在环上的边,我们放在环的最高处处理
方法就是从最低点到最高点dp一下(注意我们之前的时候是没有把这些环边计入父亲答案的)
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for(int i=h;i<=t;i++) #define dep(i,t,h) for(int i=t;i>=h;i--) #define ll long long #define me(x) memset(x,0,sizeof(x)) namespace IO{ char ss[1<<24],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++; } template<class T> void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48); while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } char sr[1<<24],z[20]; int Z,C1=-1; template<class T>void wer(T x) { if (x<0) sr[++C1]='-',x=-x; while (z[++Z]=x%10+48,x/=10); while (sr[++C1]=z[Z],--Z); } IL void wer1() { sr[++C1]=' '; } IL void wer2() { sr[++C1]=' '; } template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;} template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} template<class T>IL T MAX(T x,T y){return x>y?x:y;} template<class T>IL T MIN(T x,T y){return x<y?x:y;} }; using namespace IO; const int N=2e6; int head[N],l,n,m; struct re{ int a,b; }e[N*2]; IL void arr(int x,int y) { e[++l].a=head[x]; e[l].b=y; head[x]=l; } int f[N][2],cnt,fa[N],dfn[N],low[N]; void dfs(int x,int y) { dfn[x]=low[x]=++cnt; fa[x]=y; f[x][0]=0; f[x][1]=1; for (rint u=head[x];u;u=e[u].a) { int v=e[u].b; if (v==y) continue; if (low[v]==dfn[x]) { int f0=0,f1=0; for (int j=v;j!=x;j=fa[j]) { int t0=f0,t1=f1; f1=t0+f[j][1]; f0=MAX(t0,t1)+f[j][0]; } f[x][0]+=MAX(f0,f1); f0=0; f1=0; for (int j=v;j!=x;j=fa[j]) { int t0=f0,t1=f1; f1=t0+f[j][1]; f0=MAX(t0,t1)+f[j][0]; if (j==v) f1=0; } f[x][1]+=f0; } if (!dfn[v]) { dfs(v,x); low[x]=MIN(low[x],low[v]); } if (low[v]>dfn[x]) { f[x][0]+=MAX(f[v][0],f[v][1]); f[x][1]+=f[v][0]; } mina(low[x],dfn[v]); } } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); ios::sync_with_stdio(false); cin>>n>>m; rep(i,1,m) { int x,y; cin>>x>>y; arr(x,y); arr(y,x); } dfs(1,0); cout<<MAX(f[1][0],f[1][1]); return 0; }
2.仙人掌直径
这个和上一个类似,依旧不用建出圆方树,区分环边和普通边就可以了
对于环拿个单调队列跑一下 在更新一下每个点到子树的最大值就好了
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for(int i=h;i<=t;i++) #define dep(i,t,h) for(int i=t;i>=h;i--) #define ll long long #define me(x) memset(x,0,sizeof(x)) namespace IO{ char ss[1<<24],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++; } template<class T> void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48); while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } char sr[1<<24],z[20]; int Z,C1=-1; template<class T>void wer(T x) { if (x<0) sr[++C1]='-',x=-x; while (z[++Z]=x%10+48,x/=10); while (sr[++C1]=z[Z],--Z); } IL void wer1() { sr[++C1]=' '; } IL void wer2() { sr[++C1]=' '; } template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;} template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} template<class T>IL T MAX(T x,T y){return x>y?x:y;} template<class T>IL T MIN(T x,T y){return x<y?x:y;} }; using namespace IO; const int N=2e6; int head[N],l,n,m; struct re{ int a,b; }e[N*2]; IL void arr(int x,int y) { e[++l].a=head[x]; e[l].b=y; head[x]=l; } int f[N][2],cnt,fa[N],dfn[N],low[N],ans; int a[N]; re p[N]; void dfs(int x,int y) { dfn[x]=low[x]=++cnt; fa[x]=y; f[x][0]=f[x][1]=-1; for (rint u=head[x];u;u=e[u].a) { int v=e[u].b; if (v==y) continue; if (low[v]==dfn[x]) { int cnt=0; for (int j=v;j!=x;j=fa[j]) a[++cnt]=f[j][0]; int t=0; rep(j,1,cnt) maxa(t,a[j]+MIN(j,cnt-j+1)-1); if (t>f[x][0]) f[x][1]=f[x][0],f[x][0]=t; else if (t>f[x][1]) f[x][1]=t; int len=cnt+1; a[++cnt]=0; rep(j,1,cnt) a[cnt+j]=a[cnt]; cnt*=2; int h=1;t=0; p[h]=(re){0,0}; rep(i,1,cnt) { while (h<=t&&(i-p[h].a)>(len/2)) h++; maxa(ans,i+a[i]+p[h].b); while (h<=t&&a[i]-i>=p[t].b) t--; p[++t]=(re){i,a[i]-i}; } } if (!dfn[v]) { dfs(v,x); low[x]=MIN(low[x],low[v]); } if (low[v]>dfn[x]) { if (f[v][0]>f[x][0]) { f[x][1]=f[x][0]; f[x][0]=f[v][0]; } else if (f[v][0]>f[x][1]) f[x][1]=f[v][0]; } mina(low[x],dfn[v]); } maxa(ans,f[x][0]+f[x][1]+2); f[x][0]++; } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); while (cin>>n>>m) { ans=0; me(head); l=0; me(low); me(dfn); me(f); rep(i,1,m) { int k,x,y; cin>>k>>x; rep(j,1,k-1) { cin>>y; arr(x,y); arr(y,x); x=y; } } dfs(1,0); cout<<ans<<endl; } return 0; }
3.求仙人掌两点间最短路
这个需要建立出真的圆方树
连边的时候保证了dfn最小的点在方点的上面,而其他点在方点的下面
定义圆点与方点间的距离为当前点到dfn最小点的距离
然后查最短路的时候
如果lca是圆点,那么就是他们之间距离
如果lca是方点,那么说明是两边儿子(这个可以倍增找)在环上距离+他们到两边的距离
查找环上两点距离我用的是map维护 $M[i][j]$ 在第i个环上j的前缀和
#我这个代码在有重边的时候是错的,因为要判父亲的边表而不是点。。(虽然数据并没有重边)
稍微改一下就可以了
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for(int i=h;i<=t;i++) #define dep(i,t,h) for(int i=t;i>=h;i--) #define ll long long #define me(x) memset(x,0,sizeof(x)) #define mp make_pair namespace IO{ char ss[1<<24],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++; } template<class T> void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48); while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } char sr[1<<24],z[20]; int Z,C1=-1; template<class T>void wer(T x) { if (x<0) sr[++C1]='-',x=-x; while (z[++Z]=x%10+48,x/=10); while (sr[++C1]=z[Z],--Z); } IL void wer1() { sr[++C1]=' '; } IL void wer2() { sr[++C1]=' '; } template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;} template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} template<class T>IL T MAX(T x,T y){return x>y?x:y;} template<class T>IL T MIN(T x,T y){return x<y?x:y;} }; using namespace IO; const int N=1e5; int n,m,q,head[N],l,cnt,color_cnt,fa[N],fa2[N],dfn[N],low[N]; ll dis[N]; struct re{ int a,b,c,from; bool t; }e[N*2]; struct re2{ int a; ll b; }; map<pair<int,int>,ll> M; struct s2{ int bz[21][N],dep[N],l,head[N]; ll p[N],bz2[21][N]; re e[N*2]; void ycl() { rep(i,1,20) rep(j,1,n+color_cnt) bz[i][j]=bz[i-1][bz[i-1][j]], bz2[i][j]=bz2[i-1][j]+bz2[i-1][bz[i-1][j]]; } IL void arr2(int x,int y,int z) { e[++l].a=head[x]; e[l].b=y; e[l].c=z; head[x]=l; } void dfs(int x,int y) { dep[x]=dep[y]+1; for (rint u=head[x];u;u=e[u].a) { int v=e[u].b; if (v!=y) { bz[0][v]=x; bz2[0][v]=e[u].c; dfs(v,x); } } } re2 lca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); ll ans=0; dep(i,20,0) if (dep[bz[i][x]]>=dep[y]) ans+=bz2[i][x],x=bz[i][x]; if (x==y) return((re2){x,ans}); dep(i,20,0) if (bz[i][x]!=bz[i][y]) ans+=bz2[i][x]+bz2[i][y],x=bz[i][x],y=bz[i][y]; ans+=bz2[0][x]+bz2[0][y]; return (re2){bz[0][x],ans}; } re2 jump(int x,int y) { if (dep[x]==y) return((re2){x,0}); ll ans=0; dep(i,20,0) if (dep[bz[i][x]]>y) ans+=bz2[i][x],x=bz[i][x]; ans+=bz2[0][x],x=bz[0][x]; return (re2){x,ans}; } int query(int x,int y) { re2 k=lca(x,y); if (k.a<=n) return(k.b); ll ans=0; re2 k1=jump(x,dep[k.a]+1); re2 k2=jump(y,dep[k.a]+1); ans=k1.b+k2.b; ll len1=abs(M[mp(k.a,k1.a)]-M[mp(k.a,k2.a)]); len1=MIN(len1,p[k.a]-len1); return ans+len1; } }S; IL void arr(int x,int y,int z) { e[++l].a=head[x]; e[l].from=x; e[l].b=y; e[l].c=z; head[x]=l; } void dfs(int x,int k,int y) { dfn[x]=low[x]=++cnt; fa[x]=y; fa2[x]=k; for (rint u=head[x];u;u=e[u].a) { int v=e[u].b; if (v==y) continue; if (!dfn[v]) { dis[v]=dis[x]+e[u].c; dfs(v,u,x); mina(low[x],low[v]); } if (dfn[v]<dfn[x]) { color_cnt++; ll len1=0,len2=0; for(int j=x;j!=v;j=fa[j]) len2+=e[fa2[j]].c; len1=e[u].c; S.p[color_cnt]=len1+len2; for (int j=x;j!=v;j=fa[j]) { M[mp(color_cnt,j)]=len1; S.arr2(color_cnt,j,MIN(len1,len2)), S.arr2(j,color_cnt,MIN(len1,len2)); e[fa2[j]].t=1; if (fa2[j]%2==1) e[fa2[j]+1].t=1; else e[fa2[j]-1].t=1; len1+=e[fa2[j]].c; len2-=e[fa2[j]].c; } e[u].t=1; if (u%2==1) e[u+1].t=1; else e[u-1].t=1; S.arr2(color_cnt,v,0),S.arr2(v,color_cnt,0); } mina(low[x],dfn[v]); } } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); read(m); read(q); rep(i,1,m) { int x,y,z; read(x); read(y); read(z); arr(x,y,z); arr(y,x,z); } color_cnt=n; dfs(1,0,0); rep(i,1,l) if (!e[i].t) S.arr2(e[i].from,e[i].b,e[i].c); S.dfs(1,0); S.ycl(); rep(i,1,q) { int x,y; read(x); read(y); wer(S.query(x,y)); wer2(); } fwrite(sr,1,C1+1,stdout); return 0; }
数据生成器
#include <bits/stdc++.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for (int i=h;i<=t;i++) #define dep(i,t,h) for (int i=t;i>=h;i--) #define me(x) memset(x,0,sizeof(x)) const int N=10000; bool tf[N],now[N]; int fa[N]; bool pd(int x,int y) { if (fa[x]==y||fa[y]==x||x==y) return(0); me(tf); tf[0]=1; int i; for (i=x;i;i=fa[i]) tf[i]=1; for (i=y;tf[i]!=1;i=fa[i]) if (now[i]) return(0); for (int j=x;j!=i;j=fa[j]) if (now[j]) return(0); return(1); } void cl(int x,int y) { me(tf); tf[0]=1; int i; for (i=x;i;i=fa[i]) tf[i]=1; for (i=y;tf[i]!=1;i=fa[i]) now[i]=1; for (int j=x;j!=i;j=fa[j]) now[j]=1; } int main() { freopen("1.in","w",stdout); srand(time(0)^size_t(new char)); int n=30,m=32,q=100; cout<<n<<" "<<m<<" "<<q<<endl; rep(i,2,n) { int x=rand()%(i-1)+1; int z=rand()%10+1; fa[i]=x; cout<<x<<" "<<i<<" "<<z<<endl; } rep(i,1,m-n+1) { int x=rand()%n+1,y=rand()%n+1; int z=rand()%10+1; while (!pd(x,y)) { x=rand()%n+1,y=rand()%n+1; } cl(x,y); cout<<x<<" "<<y<<" "<<z<<endl; } rep(i,1,q) { int x=rand()%n+1,y=rand()%n+1; cout<<x<<" "<<y<<endl; } return 0; }
广义圆方树