zoukankan      html  css  js  c++  java
  • 【BZOJ1791】[IOI2008] 岛屿(水题)

    点此看题面

    大致题意: 求基环树森林中所有基环树直径之和。

    前言

    这就是一道大水题。。。

    我也不知道为什么一开始写了两个大(Bug)结果一交还有(75)分。。。

    看来这题不光题很水,数据也很水。

    环上的答案

    显然,我们可以分类讨论,把基环树的路径分成经过环的路径和不在环上的路径。

    考虑如何求出环上的答案。

    我们可以求出环上每个点子树内最长链的长度(Mx_i),以及环上前(i)条边长度的前缀和(sum_i)

    然后我们枚举一个(i)作为经过环的路径的一个关键点(即由环到树的转折点),则我们只需考虑小于(i)的点作为另一个关键点时的答案。(因为大于(i)的点作为关键点的情况会在枚到那个点的时候讨论)

    对于两个关键点(i,j),我们有答案为:

    [max(sum_{i-1}-sum_{j-1},sum_n-sum_{i-1}+sum_{j-1})+Mx_i+Mx_j ]

    考虑最终答案也是取最大值的,因此我们完全可以把这个式子中的(max)拆成两项分别算答案:

    [(sum_{i-1}+Mx_i)+(-sum_{j-1}+Mx_j) ]

    [(-sum_{i-1}+Mx_i)+(sum_n+sum_{j-1}+Mx_j) ]

    这里我已经把和(i)有关的项以及与(i)无关的项分开了,不难发现这道题还是很良心,没有同时与(i,j)有关的项(不然就要斜率优化,说起来也好久没写过这东西了)。

    则我们直接在枚举(i)的同时维护好(-sum_{j-1}+Mx_j)(sum_n+sum_{j-1}+Mx_j)最大值即可迅速求出答案。

    不在环上的答案

    不在环上,那么只能在某一子树中,于是问题就变成了树的直径。。。

    然后考虑反正我们都已经写了一个子树最长链了,干脆就用(DP)求树的直径了。

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000000
    #define LL long long
    #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,ee,lnk[N+5],vis[N+5];struct edge {int to,nxt,val;}e[2*N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    }F;
    class CircleTreeSolver
    {
    	private:
    		int cnt,s[N+5],p[N+5],dep[N+5],fa[N+5],used[N+5];
    		LL ans,cur,sum[N+5],Mx[N+5],f[N+5],g[N+5],k[N+5];
    		I bool Find(CI x)//找环
    		{
    			for(RI i=lnk[x],u,v,t=0;i;i=e[i].nxt)
    			{
    				if(e[i].to==fa[x]&&!t++) continue;//注意可能有重边
    				if(!dep[v=e[i].to]) {if(dep[v]=dep[fa[v]=x]+1,Find(v)) return 1;continue;}
    				dep[u=x]<dep[v]&&swap(u,v);W(dep[u]^dep[v]) p[s[++cnt]=u]=1,u=fa[u];//先跳到同样深度
    				RI tmp=v,tot=0;W(p[s[++cnt]=u]=1,u^v) u=fa[u],v=fa[v],++tot;//往上跳,存下一边的点
    				v=tmp;W(tot) p[s[cnt+tot]=v]=1,v=fa[v],--tot;return 1;//再次上跳,存下另一边的点
    			}return 0;
    		}
    		I LL dfs1(CI x)//树形DP求树的直径,第一遍dfs(顺带求出子树最长链)
    		{
    			LL t;vis[x]=1;for(RI i=lnk[x];i;i=e[i].nxt) !p[e[i].to]&&!vis[e[i].to]&&
    				(t=dfs1(e[i].to)+e[i].val,f[x]<t?(g[x]=f[x],f[x]=t,k[x]=e[i].to):Gmax(g[x],t));
    			return f[x];
    		}
    		I void dfs2(CI x,Con LL& v=0,CI lst=0)//树形DP求树的直径,第二遍dfs
    		{
    			Gmax(cur,f[x]+max(g[x],v));for(RI i=lnk[x];i;i=e[i].nxt) !p[e[i].to]&&
    				e[i].to^lst&&(dfs2(e[i].to,max(e[i].to^k[x]?f[x]:g[x],v)+e[i].val,x),0);
    		}
    	public:
    		I void Solve(CI x)
    		{
    			RI i,j;for(cnt=cur=0,dep[x]=1,Find(x),s[cnt+1]=s[1],i=1;i<=cnt;++i)//枚举环上点
    			{
    				for(j=lnk[s[i]];(e[j].to^s[i+1])||used[j+1>>1];j=e[j].nxt);//找下一条边
    				sum[i]=sum[i-1]+e[j].val,used[j+1>>1]=1,Mx[i]=dfs1(s[i]),dfs2(s[i]);//计算边的前缀和,记下子树内最长链长度
    			}
    			LL t1=0,t2=0;for(i=1;i<=cnt;++i)//统计环上答案
    				Gmax(cur,t1+sum[i-1]+Mx[i]),Gmax(cur,t2-sum[i-1]+Mx[i]),//更新答案
    				Gmax(t1,-sum[i-1]+Mx[i]),Gmax(t2,sum[cnt]+sum[i-1]+Mx[i]);//维护辅助信息
    			ans+=cur;//统计最终答案(因为是森林)
    		}
    		I void Print() {printf("%lld
    ",ans);}
    }S;
    int main()
    {
    	RI i,x,y;for(F.read(n),i=1;i<=n;++i) F.read(x),F.read(y),add(i,x,y),add(x,i,y);
    	for(i=1;i<=n;++i) if(!vis[i]) S.Solve(i);return S.Print(),0;
    }
    
  • 相关阅读:
    OpenCV 2.48配置
    win进入当前文件夹,启动当前文件夹的程序
    C++程序运行效率的10个简单方法
    银行国际清算业务平台架构
    股票证券交易系统架构分析与设计
    负载均衡|六种负载均衡算法
    Intelli IDEA快捷键(配合IdeaVim)(转)
    [易学易懂系列|golang语言|零基础|快速入门|(三)]
    [易学易懂系列|golang语言|零基础|快速入门|(二)]
    [易学易懂系列|golang语言|零基础|快速入门|(一)]
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1791.html
Copyright © 2011-2022 走看看