环套树
bzoj1040 騎士
題目大意:給出每個騎士的攻擊力和痛恨的人,每個騎士不能和自己痛恨的人一起出現,選一些騎士使得他們的攻擊力最大。
思路:因為每個騎士恨一個人,所以是一個環套樹森林(注意n點n邊的聯通圖可能是環套樹森林!!!),雖然題目是單向邊,但等同于雙向邊。對于環套樹dp的做法,可以搜到環后,把環從一處斷開,對于這道題目,對斷開后的兩點分別為根做樹型dp(要求根不能選),加給答案。但是要注意二元環的情況,我們可以對于二元環只加一次無向邊(其實是兩條有向邊),因為二元環完全可以當作有邊相連的兩點處理,這個時候可能會出現一棵沒有環的樹,我們需要在退回到dfs起點的那個點的時候人為的做一下dp。
一定要十分注意二元環的情況!!!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define maxnode 1000005 using namespace std; int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},tot=0,x=0,y=0,ai[maxnode]={0}; LL val[maxnode]={0},f[maxnode][2]={0},ans=0; bool visit[maxnode]={false},ff; void add(int u,int v) {++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;++tot;next[tot]=point[v];point[v]=tot;en[tot]=u; } void dp(int u,int fa) {int i,j;f[u][1]=val[u];visit[u]=true;for (i=point[u];i;i=next[i]){if (en[i]!=fa){if ((u==y&&en[i]==x)||(u==x&&en[i]==y)) continue;dp(en[i],u);f[u][0]+=max(f[en[i]][0],f[en[i]][1]);f[u][1]+=f[en[i]][0];}} } void dfs(int u,int fa) {int i,j;LL ci=0;visit[u]=true;for (i=point[u];i;i=next[i]){if (en[i]!=fa){if (visit[en[i]]){x=u;y=en[i];memset(f,0,sizeof(f));dp(x,y);ci=max(ci,f[x][0]);memset(f,0,sizeof(f));swap(x,y);dp(x,y);ci=max(ci,f[x][0]);ans+=ci;ff=true;return;}else{dfs(en[i],u);if (ff) return;}}}if (fa==0){x=y=0;dp(u,0);ans+=max(f[u][0],f[u][1]);} } int main() {int n,i,j;scanf("%d",&n);for (i=1;i<=n;++i) scanf("%I64d%d",&val[i],&ai[i]);for (i=1;i<=n;++i)if (ai[ai[i]]!=i||ai[i]>i) add(i,ai[i]);for (i=1;i<=n;++i)if (!visit[i]){ff=false;dfs(i,0);}printf("%I64d\n",ans); } View Code?
bzoj2878 迷失游樂園(!!)
題目大意:給出一棵環套樹,隨機選一個點開始走,等概率走向周圍沒有經過的點,問走的路徑長度期望。
思路:樹上的比較好求,從終點開始走,更新出每個點的up和down,gi[u][0]=sigma 1/(du[u]-1)*(gi[v][0]+va[i]),最后一個點的時候(也就是起點)是/du[u]。有環的時候,先找出所有外向樹的up,用這個up去更新其他的up(中間要用另一個數組存,防止改變最初的up)。有環的時候的概率是(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc。cc表示環上選這條路徑的概率,ci表示環上鏈的權值和,ci后面乘的式子是因為外向樹中所帶來的概率的和不是樹上的(du[u]-1)/du[u],而是(du[u]-2)/du[u],所以有所改變。對于環上相鄰兩點繞了一圈的情況終點的處的概率是1/(du[u]-2),因為環上的兩個點都不能走。
注意:一定要考慮好概率的轉移和環上相鄰點的特殊情況。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 200005 #define LD double #define eps 1e-9 using namespace std; int in(){char ch=getchar();int x=0;while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;} int cmp(LD x,LD y){if (x-y>eps) return 1;if (y-x>eps) return -1;return 0;} int n,m,rt,point[N]={0},next[M],en[M],tot,du[N]={0},fe[N]={0},hu[N],hz=0,zh[N],zt=0; LD va[M],fi[N],gi[N][2],hv[N]; bool oh[N]={false},hh=false,vi[N]={false}; void add(int u,int v,LD w){next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w;next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w;++du[u];++du[v]; } void up(int u,int fa){int i,v;gi[u][0]=0.;for (i=point[u];i;i=next[i]){if ((v=en[i])==fa) continue;up(v,u);gi[u][0]+=1./(du[u]-1)*(gi[v][0]+va[i]);} } void down(int u,int fa,LD vv){int i,v;fi[u]=0.;gi[u][1]=gi[u][0];if (fa){fi[u]+=1./du[u]*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);if (du[u]>1) gi[u][1]+=1./(du[u]-1)*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);}for (i=point[u];i;i=next[i]){if ((v=en[i])==fa) continue;down(v,u,va[i]);fi[u]+=1./du[u]*(gi[v][0]+va[i]);} } void geth(int x){hz=0;for (;;--zt){oh[zh[zt]]=true;hu[++hz]=zh[zt];hv[hz]=va[fe[zh[zt]]];if (zh[zt]==x) break;} } void find(int u){int i,v;zh[++zt]=u;vi[u]=true;for (i=point[u];i;i=next[i]){v=en[i];if (i==fe[u]||(i==(fe[u]^1))) continue;if (vi[v]){geth(v);hh=true;hv[hz]=va[i];return;}fe[v]=i;find(v);if (hh) return;}--zt; } void gup(int u){int i,v;gi[u][0]=0.;vi[u]=true;for (i=point[u];i;i=next[i]){if (vi[v=en[i]]) continue;gup(v);gi[u][0]+=1./(du[u]-1)*(gi[v][0]+va[i]);} } void gdown(int u,int fa,LD vv){int i,v;gi[u][1]=gi[u][0];if (fa){fi[u]+=1./du[u]*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);if (du[u]>1) gi[u][1]+=1./(du[u]-1)*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);}for (i=point[u];i;i=next[i]){v=en[i];if (oh[v]||v==fa) continue;gdown(v,u,va[i]);fi[u]+=1./du[u]*(gi[v][0]+va[i]);} } int main(){int i,j,u,v,w,la;LD ans=0.,ci,cc;n=in();m=in();tot=1;for (i=1;i<=m;++i){u=in();v=in();w=in();add(u,v,(LD)w);}if (m==n-1){for (i=1;i<=n;++i)if (du[i]>1){rt=i;break;}up(rt,0);down(rt,0,0.);for (i=1;i<=n;++i) ans+=1./n*fi[i];}else{find(1);for (i=1;i<=n;++i){vi[i]=oh[i];fi[i]=gi[i][0]=gi[i][1]=0.;}for (i=1;i<=n;++i)if (oh[i]) gup(i);for (i=1;i<=hz;++i){u=hu[i];ci=0.;cc=1.;gi[u][1]+=gi[u][0];for (la=i,j=i%hz+1;j!=i;la=j,j=j%hz+1){ci+=hv[la];cc/=1.*(du[hu[j]]-1);if (cmp(gi[u][0],0.)>0){if (j%hz+1!=i){fi[hu[j]]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc*(du[hu[j]]-1)/du[hu[j]];gi[hu[j]][1]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc;}else{fi[hu[j]]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc*(du[hu[j]]-1)/du[hu[j]];gi[hu[j]][1]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc;}}}if (du[u]==2){fi[hu[la]]+=ci*cc*(du[hu[la]]-1)/du[hu[la]];gi[hu[la]][1]+=ci*cc;}ci=0.;cc=1.;for (la=i,j=(i==1 ? hz : i-1);j!=i;la=j,j=(j==1 ? hz : j-1)){ci+=hv[j];cc/=1.*(du[hu[j]]-1);if (cmp(gi[u][0],0.)>0){if ((j==1 ? hz : j-1)!=i){fi[hu[j]]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc*(du[hu[j]]-1)/du[hu[j]];gi[hu[j]][1]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc;}else{fi[hu[j]]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc*(du[hu[j]]-1)/du[hu[j]];gi[hu[j]][1]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc;}}}if (du[u]==2){fi[hu[la]]+=ci*cc*(du[hu[la]]-1)/du[hu[la]];gi[hu[la]][1]+=ci*cc;}}for (i=1;i<=n;++i)if (oh[i]){gi[i][0]=gi[i][1];gi[i][1]=0.;}for (i=1;i<=n;++i)if (oh[i]) gdown(i,0,0.);for (i=1;i<=n;++i) ans+=1./n*fi[i];}printf("%.5f\n",ans); } View Code?
bzoj3242 快餐店
題目大意:給出一棵環套樹,在某個位置(邊或點上)放一個快餐店,使得最遠點到這個店的距離最小,輸出最小距離。
思路:可以證明方案中一定有一條環上的邊不經過(邊左右的點分別經過左右到那個快餐店或者快餐店所在外向樹的根!!)。所以可以刪掉一條邊之后,求出樹的直徑/2就是答案,考慮快速求這個直徑。如果在外向樹上,是不變的,可以dp出一個值mx(這一部分不要忘記!!)。經過環的話,是不相同的兩點i、j,距離是max(sm[i]-sm[j]+fi[i]+fi[j]),sm[i]表示不選邊的一端為起點的邊權和,fi[i]表示i這個點外向樹從根出去的最長鏈,可以維護最大的sm[i]+fi[i],最小的sm[i]-fi[i],因為i和j不能相同,維護出次大次小值,求的時候判斷一下。取刪掉不同邊的直徑的最小值。
注意:1)分清最大最小值:外向樹上的直徑是要和環上直徑取max的;環上求直徑的時候,如果最大最小值的i一樣,用最大次小和最小次大的較大值更新;
2)這里的sum只需要求相對差就可以了,不用區間修改,只需要用上一個+邊權更新這一個就可以了。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define N 100005 #define M 200005 #define LL long long #define mid (l+r)/2 #define lson i<<1,l,mid #define rson i<<1|1,mid+1,r #define inf 1000000000000000000LL using namespace std; int in(){char ch=getchar();int x=0;while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;} int point[N]={0},next[M],en[M],tot=0,zh[N],zt=0,hu[N],hz,fe[N]={0}; LL hv[N],va[M],fi[N]={0},dis[N]={0},sm[N]={0},ld=0LL,gi[N]={0}; bool vi[N]={false},oh[N]={false},f=false; void add(int u,int v,LL w){next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w;next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w; } void geth(int x){for (hz=0;;--zt){hu[++hz]=zh[zt];oh[zh[zt]]=true;hv[hz]=va[fe[zh[zt]]];if (zh[zt]==x) break;} } void dfs(int u,int fa){int i,v;vi[u]=true;zh[++zt]=u;for (i=point[u];i;i=next[i]){if ((v=en[i])==fa) continue;if (vi[v]){geth(v);f=true;hv[hz]=va[i];return;}fe[v]=i;dfs(v,u);if (f) return;}--zt; } void getf(int u,int anc){int i,v;LL mx=0LL,cmx=0LL,vv;vi[u]=true;gi[u]=0LL;fi[anc]=max(fi[anc],dis[u]);for (i=point[u];i;i=next[i]){if (vi[v=en[i]]) continue;dis[v]=dis[u]+va[i];getf(v,anc);vv=gi[v]+va[i];gi[u]=max(gi[u],vv);if (vv>=mx){cmx=mx;mx=vv;}else if (vv>=cmx) cmx=vv;}ld=max(ld,mx+cmx); } struct use{LL mx,mn,cmx,cmn;int px,pn,cpx,cpn;}tr[N<<2]; use updata(use x,use y){if (y.mx>=x.mx){x.cmx=x.mx;x.cpx=x.px;x.mx=y.mx;x.px=y.px;}else if (y.mx>=x.cmx){x.cmx=y.mx;x.cpx=y.px;}if (y.cmx>=x.cmx){x.cmx=y.cmx;x.cpx=y.cpx;}if (y.mn<=x.mn){x.cmn=x.mn;x.cpn=x.pn;x.mn=y.mn;x.pn=y.pn;}else if (y.mn<=x.cmn){x.cmn=y.mn;x.cpn=y.pn;}if (y.cmn<=x.cmn){x.cmn=y.cmn;x.cpn=y.cpn;}return x;} void build(int i,int l,int r){if (l==r){tr[i]=(use){sm[l]+fi[l],sm[l]-fi[l],-inf,inf,l,l,0,0};return;}build(lson);build(rson);tr[i]=updata(tr[i<<1],tr[i<<1|1]); } void tch(int i,int l,int r,int x){if (l==r){tr[i]=(use){sm[l]+fi[l],sm[l]-fi[l],-inf,inf,l,l,0,0};return;}if (x<=mid) tch(lson,x);else tch(rson,x);tr[i]=updata(tr[i<<1],tr[i<<1|1]); } int main(){int n,i,j,k,u,v,w;LL ans=inf;use ci;n=in();for (i=1;i<=n;++i){u=in();v=in();w=in();add(u,v,(LL)w);}dfs(1,0);for (i=1;i<=n;++i) vi[i]=oh[i];for (i=1;i<=hz;++i) getf(hu[i],i);for (i=1;i<=hz;++i) sm[i]=sm[i-1]+hv[i-1];build(1,1,hz);for (i=1;i<=hz;++i){if (i>1){j=i-1;k=(j==1 ? hz : j-1);sm[j]=sm[k]+hv[k];tch(1,1,hz,j);}ci=tr[1];if (ci.px!=ci.pn) ans=min(ans,ci.mx-ci.mn);else ans=min(ans,max(ci.mx-ci.cmn,ci.cmx-ci.mn));}printf("%.1f\n",1.*max(ans,ld)/2.); } View Code?
轉載于:https://www.cnblogs.com/Rivendell/p/4760952.html
總結
- 上一篇: Kruskal HDOJ 4313 Ma
- 下一篇: Xposed学习一:初探