bzoj 1791: [Ioi2008]Island 岛屿【基环树+单调队列优化dp】

我太菜了居然调了一上午……

这个题就是要求基环树森林的基环树直径和

大概步骤就是找环—>dp找每个环点最远能到达距离作为点权—>复制一倍环,单调队列dp

找环是可以拓扑的,但是利用性质有更快好写的做法,就是像朱刘算法找环那样,按照输入的方向(i—>to_i)打一圈标记,如果碰到同样标记就说明有环,这里注意我一开始没注意到的,从i点进入找到环不代表i点在环上,因为可能是6字形的,所以一定是环点的是找到的有同样标记的那个点,然后顺着这个点把环点都放进一个栈(其实不用,但是这样好写一些),顺着每个点向非环点dfs找到最远点,这里注意!非常坑的是一棵基环树的直径不一定经过环,所以在dfs过程中,把每个点向下的最长和次长路径的和都和一个全局变量mx取max。把每个点向下最长当做点权va[i]

这样就只剩一个环长为len的环了,把这个环复制一遍,sum表示长度前缀和,答案就是max(va[i]+va[j]+sum[j]-sum[i])(j-i<len),这个可以用单调队列实现,能得到答案ans

这样,max(ans,mx)就是当前这棵基环树的直径了

把每次找到的环的答案加起来即可

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1000005;
int n,ne[N],le[N],h[N],cnt,v[N],s[N],top,q[N];
long long l[N],va[N<<1],ans,sm[N<<1],mxx;
struct qwe
{
    int ne,to,va;
}e[N<<1];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>‘9‘||p<‘0‘)
    {
        if(p==‘-‘)
            f=-1;
        p=getchar();
    }
    while(p>=‘0‘&&p<=‘9‘)
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
void add(int u,int v,int w)
{
    cnt++;
    e[cnt].ne=h[u];
    e[cnt].to=v;
    e[cnt].va=w;
    h[u]=cnt;
}
long long dfs(int u,int fa,int f)
{
    long long mx1=0,mx2=0;
    for(int i=h[u];i;i=e[i].ne)
        if(v[e[i].to]!=-1&&e[i].to!=fa)
        {
            long long w=dfs(e[i].to,u,f)+e[i].va;
            if(w>mx1)
                mx2=mx1,mx1=w;
            else if(w>mx2)
                mx2=w;
        }
    mxx=max(mxx,mx1+mx2);
    return mx1;
}
long long dp()
{
    for(int i=1;i<=top;i++)
        sm[i]=sm[i-1]+l[i];
    for(int i=top+1;i<=top*2;i++)
        sm[i]=sm[i-1]+l[i-top],va[i]=va[i-top];
    int l=1,r=1;q[1]=1;
    long long ans=0;
    for(int i=2;i<=top*2;i++)
    {
        while(l<r&&i-q[l]>=top)
            l++;
        ans=max(ans,sm[i]+va[i]-(sm[q[l]]-va[q[l]]));
        while(l<r&&sm[q[r]]-va[q[r]]>sm[i]-va[i])
            r--;
        q[++r]=i;
    }
    return ans;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        ne[i]=read(),le[i]=read();
        add(i,ne[i],le[i]),add(ne[i],i,le[i]);
    }
    for(int i=1,j;i<=n;i++)
        if(!v[i])
        {
            top=0;
            for(j=i;!v[j];j=ne[j])
                v[j]=i;
            if(v[j]!=i)
                continue;
            s[++top]=j;
            for(int u=ne[j];u!=j;u=ne[u])
                s[++top]=u;
            for(j=1;j<=top;j++)
                v[s[j]]=-1;
            for(j=1;j<=top;j++)
                l[j+1]=le[s[j]];
            l[1]=l[top+1];
            long long mx=0;
            for(j=1;j<=top;j++)
            {
                mxx=0;
                va[j]=dfs(s[j],0,i);
                mx=max(mx,mxx);
            }
            ans+=max(dp(),mx);
            for(j=1;j<=top;j++)
                v[s[j]]=i;
        }
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/9280061.html

时间: 2024-10-01 04:52:29

bzoj 1791: [Ioi2008]Island 岛屿【基环树+单调队列优化dp】的相关文章

[bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)

bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_x$ 之后记录环上边长前缀和$ns_i$ dp值为$max_{i,j}dp[i]+sum[i]+dp[j]-sum[j]$ $dp[j]-sum[j]$提出来进单调队列. O(n). 记得dfs改bfs. #include<cstdio> #include<algorithm> using namespace std; typedef long long lin

BZOJ 1855 股票交易(单调队列优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1855 题意:最近lxhgww又迷上了投资股票, 通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每 个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BS

BZOJ 1855 [Scoi2010]股票交易 单调队列优化DP

题意:链接 方法:单调队列优化DP 解析:噢又是一道情况很多的题,然而三种更新我又落下一种导致样例不过,后来看题解才恍然- -最SB的一种更新居然忘了. 状态好想f[i][j]代表前i天有j双袜子时的最大利润. 三种更新: 第一种:f[i][j]=max(f[i][j],f[i?1][j]):(然而我忘了这一种) 第二种:买入f[i][j]=max(f[i][j],f[i?w?1][k]?(j?k)?a[i].ap)(k>=j?a[i].as); 第三种:卖出f[i][j]=max(f[i][j

【笔记篇】单调队列优化dp学习笔记&amp;&amp;luogu2569_bzoj1855股票交♂易

DP颂 DP之神 圣洁美丽 算法光芒照大地 我们怀着 崇高敬意 跪倒在DP神殿里 你的复杂 能让蒟蒻 试图入门却放弃 在你光辉 照耀下面 AC真心不容易 dp大概是最经久不衰 亘古不化的算法了吧. 而且有各种各样的类型 优化之类的. 一直dp都不怎么好. 而且也不太知道应该怎么提高. 基本见到不认识的dp方程就不大会推(但我会打表啊= = 所以dp还是很有的学的~ 正好最近刚刚肝了计算几何, 所以就顺带搞一下斜率优化dp一类的... 单调队列优化dp 单调队列大家都会吧? 不会的先出去学一下,

HDU 4122 Alice&#39;s mooncake shop 单调队列优化dp

Alice's mooncake shop Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4122 Description The Mid-Autumn Festival, also known as the Moon Festival or Zhongqiu Festival is a popular harvest festival celebrated by Ch

Tyvj1305最大子序和(单调队列优化dp)

描述 输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大. 例如 1,-3,5,1,-2,3 当m=4时,S=5+1-2+3=7当m=2或m=3时,S=5+1=6 输入格式 第一行两个数n,m第二行有n个数,要求在n个数找到最大子序和 输出格式 一个数,数出他们的最大子序和 测试样例1 输入 6 4 1 -3 5 1 -2 3 输出 7 备注 数据范围:100%满足n,m<=300000 是不超过m,不是选m个!!!!! /* 单调队列优化dp 单调队列维护的是前

bzoj1855: [Scoi2010]股票交易--单调队列优化DP

单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w-1][k]+k*Ap[i]的单调性即可 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 2010; 6 int

1855: [Scoi2010]股票交易[单调队列优化DP]

1855: [Scoi2010]股票交易 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1083  Solved: 519[Submit][Status][Discuss] Description 最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=

【单调队列优化dp】uestc 594 我要长高

http://acm.uestc.edu.cn/#/problem/show/594 [AC] 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=5e4+2; 5 const int inf=0x3f3f3f3f; 6 int n,c; 7 int cur; 8 int dp[2][maxn]; 9 int q[maxn]; 10 int main() 11 { 1