bzoj1040 骑士
题目大意:给出每个骑士的攻击力和痛恨的人,每个骑士不能和自己痛恨的人一起出现,选一些骑士使得他们的攻击力最大。
思路:因为每个骑士恨一个人,所以是一个环套树森林(注意n点n边的联通图可能是环套树森林!!!),虽然题目是单向边,但等同于双向边。对于环套树dp的做法,可以搜到环后,把环从一处断开,对于这道题目,对断开后的两点分别为根做树型dp(要求根不能选),加给答案。但是要注意二元环的情况,我们可以对于二元环只加一次无向边(其实是两条有向边),因为二元环完全可以当作有边相连的两点处理,这个时候可能会出现一棵没有环的树,我们需要在退回到dfs起点的那个点的时候人为的做一下dp。
一定要十分注意二元环的情况!!!
#include#include #include #include #define LL long long#define maxnode 1000005using 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);}
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#include #include #include #define N 100005#define M 200005#define LD double#define eps 1e-9using 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);}
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#include #include #include #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 1000000000000000000LLusing 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.);}