BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径。

对于环套树的直径,可以先找到这个环套树上面的环。然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值。

那么直径一定就是从环上的某个点开始,某个点结束的。

把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP。

剩下的就是这样依次统计每个环套树的直径之和。

对于环套树上找环可以借鉴最小树形图找环的技巧。

首先将边定向,保证每个点的出度为1.由于环套树的性质,这样从这颗树的任意点开始搜索,一定会回到原来访问过的点,在这个过程中记录好每个点的前驱。

就可以很easy的将这个环找出来。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <cctype>
#include <iostream>
#define N 1050000
using namespace std;
inline int getc() {
    static const int L = 1<<15;
    static char buf[L],*S=buf,*T=buf;
    if(S==T){
        T=(S=buf)+fread(buf,1,L,stdin);
        if(S==T)return EOF;
    }
    return *S++;
}
inline int getint() {
    int c;
    while(!isdigit(c = getc()));
    int tmp = c-‘0‘;
    while(isdigit(c=getc()))
        tmp=(tmp<<1)+(tmp<<3)+c-‘0‘;
    return tmp;
}
struct Syndra
{
    int u,v,len,next;
}e[N];
struct Fiona
{
    int edge,flag1,flag2;
    long long temp,max1,max2;
}s[N];
int head[N],cnt,n;
int visit[N],next[N],len[N];
int i,j,k;
long long sa[N],pre[N],ans;
void add(int u,int v,int len)
{
    cnt++;
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].len=len;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int que[N<<1];
long long sum[N<<1],ret;
long long dp(int num)
{
    int top,tail;
    int u,b,star;
    int et;
    for(et=1;et<(num<<1);et++)
    {
        sum[et]=sum[et-1]+pre[(et-1)>=num?(et-1-num):(et-1)];
    }
    top=tail=0;
    /*
    que[top]=0;
    for(et=1;et<(num<<1);et++)
    {
        while(et-que[top]>=num)top++;
        u=que[top];
        ret=max(ret,sa[et>=num?et-num:et]+sa[u>=num?u-num:u]+sum[et]-sum[u]);
        b=que[tail];
        que[++tail]=et;
        for(star=tail;star>top;b=que[star-1])
        {
            if(sum[et]-sum[b]+sa[b]<sa[et])
            {
                que[star]=b;
                que[--star]=et;
            }
            else break;
        }
        tail=star;
    }
    */
    que[tail++]=0;
    for(et=1;et<(num<<1);++et)
    {
        while(top<tail&&et-que[top]>=num)++top;
        u=que[top];
        ret=max(ret,sa[et>=num?et-num:et]+sa[u>=num?u-num:u]+sum[et]-sum[u]);
        while(top<tail&&sa[et>=num?et-num:et]>=sa[que[tail-1]>=num?que[tail-1]-num:que[tail-1]]+sum[et]-sum[que[tail-1]])--tail;
        que[tail++]=et;
    }
    return ret;
}
void build()
{
    cnt=1;
    memset(head,0,sizeof(head));
    memset(visit,0,sizeof(visit));
    n=getint();
    for(i=1;i<=n;i++)
    {
        next[i]=getint();
        len[i]=getint();
        add(next[i],i,len[i]);
    }
}
stack<int>sk;
int fa[N];
void dfs(int x)
{
    if(s[x].edge==0)
    {
        sk.pop();
        if(s[x].flag2)ret=max(ret,s[x].max1+s[x].max2);
        if(visit[x]==-1)
            return ;
        x = sk.top();
        {
            int v,tt=s[x].edge;
            v=e[tt].v;
            visit[v]=i;
            s[x].temp=s[v].max1+e[tt].len;
            if(s[x].max1<s[x].temp)
            {
                if(s[x].flag1)s[x].max2=s[x].max1,s[x].flag2=1;
                else s[x].flag1=1;
                s[x].max1=s[x].temp;
            }
            else if(s[x].max2<s[x].temp)s[x].max2=s[x].temp,s[x].flag2=1;
            s[x].edge=e[tt].next;
        }
        return ;
    }
    int v,tt=s[x].edge;
    v=e[tt].v;
    if(visit[v]==-1)
    {
        s[x].edge=e[tt].next;
        return ;
    }
    fa[v]=x;
    s[v].edge=head[v];
    sk.push(v);
}
long long handle(int x)
{
    s[x].edge=head[x];
    sk.push(x);
    while(!sk.empty())
    {
        dfs(sk.top());
    }
    return s[x].max1;
}/*handle(long long)+dfs(void)=dfs(long long)*/
/*long long dfs(int x)
{
    int et,v,flag1,flag2;
    long long max1,max2;
    for(max1=max2=0,flag1=flag2=0,et=head[x];et;et=e[et].next)
    {
        v=e[et].v;
        if(visit[v]==-1)continue;
        temp=dfs(v)+e[et].len;
        visit[v]=i;
        if(max1<temp)
        {
            if(flag1)max2=max1,flag2=1;
            max1=temp;
            flag1=1;
        }
        else if(max2<temp)max2=temp,flag2=1;
    }
    if(flag2)ret=max(ret,max1+max2);
    return max1;
}*/
int main()
{
    int u,v;
    build();
    for(i=1;i<=n;i++)
    {
        if(!visit[i])
        {
            for(u=i;!visit[u];u=next[u])
            {
                visit[u]=i;
            }
            if(visit[u]==i)
            {
                ret=0;cnt=0;
                visit[u]=-1;
                for(v=next[u];v!=u;v=next[v])
                {
                    visit[v]=-1;
                }
                v=u;
                do{
                    pre[cnt]=len[v];
                    sa[cnt++]=handle(v);
                    v=next[v];
                }while(v!=u);
                ans+=dp(cnt);
            }
        }
    }
    cout<<ans;
    return 0;
}  

时间: 2024-08-05 10:53:13

BZOJ 1791 岛屿(环套树+单调队列DP)的相关文章

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

我太菜了居然调了一上午-- 这个题就是要求基环树森林的基环树直径和 大概步骤就是找环->dp找每个环点最远能到达距离作为点权->复制一倍环,单调队列dp 找环是可以拓扑的,但是利用性质有更快好写的做法,就是像朱刘算法找环那样,按照输入的方向(i->to_i)打一圈标记,如果碰到同样标记就说明有环,这里注意我一开始没注意到的,从i点进入找到环不代表i点在环上,因为可能是6字形的,所以一定是环点的是找到的有同样标记的那个点,然后顺着这个点把环点都放进一个栈(其实不用,但是这样好写一些),顺着

【BZOJ-2892&amp;1171】强袭作战&amp;大sz的游戏 权值线段树+单调队列+标记永久化+DP

2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] Description 在一个没有冬马的世界里,经历了学园祭后的春希着急着想要见到心爱的雪菜.然而在排队想见雪菜的fans太多了,春希一时半会凑不到雪菜面前. 作为高帅富,这样的问题怎么能难倒春希?春希从武也手中拿到了取自金闪闪宝库里的多啦A梦的传话筒,并且给每一个排队的fans都发了一个传话筒. 于

BZOJ 1012 线段树||单调队列

非常裸的线段树  || 单调队列: 假设一个节点在队列中既没有时间优势(早点入队)也没有值优势(值更大),那么显然不管在如何的情况下都不会被选为最大值. 既然它仅仅在末尾选.那么自然能够满足以上的条件. 线段树 #include "stdio.h" #include "string.h" struct node { int l,r,Max; }data[800010]; int Max(int a,int b) { if (a<b) return b; els

CF480E Parking Lot(单调队列+dp然鹅并不是优化)

(全英文题面所以直接放化简题意) 题意:在一个二维平面内,初始有一些点,然后每个时间点加入一些点,对每个时间点求平面内最大的无障碍正方形 (这次的题目是真的神仙啊...) 首先,考虑暴力,如果对每一个加点进行一遍扫描,那么,可以跑到天荒地老了... 其次,如果像以前的dp一样跑呢?因为是动态的,所以不行... 很容易想到,这个面积一定是单调不增的,于是,就可以倒序来,变成加点,离线做. 那么,有了加点就可以跑暴力了吗?很显然不行... 那怎么办呢? solution: (以下初始思路来自老师)

POJ 3017 单调队列dp

Cut the Sequence Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8764   Accepted: 2576 Description Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subseque

[TyvjP1313] [NOIP2010初赛]烽火传递(单调队列 + DP)

传送门 就是个单调队列+DP嘛. ——代码 1 #include <cstdio> 2 3 const int MAXN = 1000001; 4 int n, m, h = 1, t = 1, ans = ~(1 << 31); 5 int q[MAXN], a[MAXN], f[MAXN]; 6 7 inline int min(int x, int y) 8 { 9 return x < y ? x : y; 10 } 11 12 int main() 13 { 14

hdu4374单调队列+dp

http://acm.hdu.edu.cn/showproblem.php?pid=4374 Problem Description Now there is a game called the new man down 100th floor. The rules of this game is: 1.  At first you are at the 1st floor. And the floor moves up. Of course you can choose which part

BZOJ 3831 POI 2014 Little Bird 单调队列DP

题目大意:给出一片树林,树排成一排,每一棵树都有一个高度.从地一棵树出发,每次可以跳到i+k棵之前,跳到小于自己高度的树上不需要花费体力,反之需要花费一点体力,问到最后一棵树最少需要多少体力. 思路:简单DP方程:f[i] = min{f[j] + (height[i] >= height[j])} 然后发现数据范围只有O(n)可以过. 维护单调队列,队列中按照f单调递减,队尾按照时间往出弹. 当f值相同的时候,高度较高的优先. CODE: #include <queue> #inclu

BZOJ 1040 骑士(环套树DP)

如果m=n-1,显然这就是一个经典的树形dp. 现在是m=n,这是一个环套树森林,破掉这个环后,就成了一个树,那么这条破开的边连接的两个顶点不能同时选择.我们可以对这两个点进行两次树形DP根不选的情况. 那么答案就是每个森林的max()之和. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # in