2017.9.8 仙人掌图 失败总结

来源:互联网 发布:微博短域名 编辑:程序博客网 时间:2024/05/06 00:12

其实这个题思路还是挺简单的、、一开始和正解只差了一个单调队列、

就是把他当成树dp处理,一开始想的拓扑序往上缩、但其实dfs就可以了。。

仙人掌的图就是由两部分构成、一个是桥、一个是环、所以对于桥我们直接用树dp

对于环我们把环上所有点到环的起点更新给起点就好了、

所以记一个low和pos判定环和桥  (←如果让我写一定维护一大堆)

然后就是环对答案贡献的处理了、

其实也很简单、把环拆成链,然后半个半个统计就不用取n-i-j和i-j的min了、(极为巧妙)

然后只用考虑f【i】+f【j】+i-j    归一下类: f【i】+i         +f【j】-j

注意我们只取半个环,所以限定范围i-j<=点数/2 

我们对每个i,需要取合法f【j】-j的最大值、就可以维护了


码:

#include<iostream> #include<cstdio>using namespace std;#include<cstring>#define N  120005int tot,hou[N<<1],xia[N],a[N],n,m,zhong[N<<1],d[N],fu[N],v[N],ans,f[N],low[N],dfn,pos[N];void jian(int a,int b){++tot,hou[tot]=xia[a],xia[a]=tot,zhong[tot]=b;}void jia(int a,int b){jian(a,b);jian(b,a);}void dp(int o,int oo){int cnt=d[oo]-d[o]+1,i,z1=1,z2=1;for(i=oo;i!=o;i=fu[i])a[cnt--]=f[i];a[1]=f[o];cnt=d[oo]-d[o]+1;for(i=1;i<=cnt;i++)a[i+cnt]=a[i];v[1]=1;for(i=2;i<=cnt+(cnt>>1);i++){  if(i-v[z1]>(cnt>>1))z1++;ans=max(ans,i+a[i]+a[v[z1]]-v[z1]);  while(z1<=z2&&a[v[z2]]-v[z2]<=a[i]-i)z2--;v[++z2]=i;}for(i=2;i<=cnt;i++)  f[o]=max(f[o],a[i]+min(i-1,cnt-i+1));}void dfs(int o,int fa,int dis){ int i;fu[o]=fa;d[o]=dis;low[o]=pos[o]=++dfn;for(i=xia[o];i!=-1;i=hou[i]){int nd=zhong[i];if(nd==fa)continue;if(!pos[nd])dfs(nd,o,dis+1);low[o]=min(low[o],low[nd]);if (low[nd]>pos[o]){  // 连不到上面的一定是桥 看看能否更新         ans=max(ans,f[nd]+f[o]+1); f[o]=max(f[o],f[nd]+1);        }  //这里是继承性dp、、很多时候需要用到(如sdoi2017 r2用它能骗不少分) }for(i=xia[o];i!=-1;i=hou[i]){int nd=zhong[i];if(fu[nd]!=o&&pos[o]<pos[nd])dp(o,nd);}}int main(){int i,x,l,j,y;memset(xia,-1,sizeof(xia));scanf("%d%d",&n,&m);for(i=1;i<=m;i++){scanf("%d",&y);int l=0;for(j=1;j<=y;j++){scanf("%d",&x);if(l!=0)jia(l,x);l=x;}}dfs(1,0,1);printf("%d",ans);}



原创粉丝点击