POI2014

...一个shabi和一堆神题的故事

今天只写了两道

之后随缘更吧

 

啊 顺便 snake我是不会更的

bzoj3829 POI2014 Farmcraft

mhy住在一棵有n个点的树的1号结点上,每个结点上都有一个妹子。
mhy从自己家出发,去给每一个妹子都送一台电脑,每个妹子拿到电脑后就会开始安装zhx牌杀毒软件,第i个妹子安装时间为Ci。
树上的每条边mhy能且仅能走两次,每次耗费1单位时间。mhy送完所有电脑后会回自己家里然后开始装zhx牌杀毒软件。
卸货和装电脑是不需要时间的。
$n \leq 500000$
求所有妹子和mhy都装好zhx牌杀毒软件的最短时间。
 
sol:一开始对着样例江了半天...然后发现妹子是可以自己装杀毒软件的
这就非常的简单了,状态还是设$f[x]$表示在$x$子树里安装好花费的时间,初始化...就是$f[x] = c[x]$
然后我们看转移的顺序,显然是可以贪心的
如果一个子树安装的比较久,我们就可以先搞它然后让它等着比较快的
具体地,对于两个子树$u$,$v$
当先走$u$再走$v$的时候时间是$f[u] + 1 + 2 \times size[u] + f[v] + 1$
当先走$u$再走$v$的时候时间是$f[v] + 1 + 2 \times size[v] + f[u] + 1$
 
这样如果$f[u] - 2 \times size[u] > f[v] - 2 \times size[v]$
我们会选择先走$u$
这样就可以对子树进行一下排序
细节:最后安装$1$号节点
#include<bits/stdc++.h>
using namespace std;
 
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
int n;
const int maxn = 500010;
int first[maxn],to[maxn << 1],nx[maxn << 1],cnt;
int val[maxn],size[maxn],fa[maxn];
int f[maxn];
int sons[maxn];
inline void add(int u,int v){to[++cnt] = v;nx[cnt] = first[u];first[u] = cnt;}
inline bool cmp(int u,int v){return max(f[u],2 * size[u] + f[v]) < max(f[v],2 * size[v] + f[u]);}
inline void dfs(int x)
{
    size[x] = 1;
    for(int i=first[x];i;i=nx[i])
    {
        if(to[i] == fa[x])continue;
        fa[to[i]] = x;dfs(to[i]);size[x] += size[to[i]];
    }
    int nt = 0,tmp = 0;
    f[x] = val[x];
    for(int i=first[x];i;i=nx[i]){if(to[i] == fa[x])continue;sons[++nt] = to[i];}
    sort(sons + 1,sons + nt + 1,cmp);
    for(int i=1;i<=nt;++i)f[x]=max(f[x],tmp*2+f[sons[i]]+1),tmp+=size[sons[i]];
}
int main()
{
    n = read();
    for(int i=1;i<=n;i++)val[i] = read();
    for(int i=1;i<n;i++)
    {
        int u = read(),v = read();
        add(u,v);add(v,u);
    }
    dfs(1);
    cout<<max(f[1],(n-1)*2+val[1]);
}
View Code

 

bzoj3832 Rally

一个DAG,每条边长度都是$1$,求删掉一个点之后最长路的最小值

输出最小值和那个点

$n \leq 500000$

$m \leq 2 \times n$

 

sol:神题

首先我们建超级源$S$,超级汇$T$,每个点向源汇连边$(S -> X -> T)$

这样图上最长链就变成了$S$到$T$最长链

我们可以拓扑序$dp$出每个点到$S$到$T$的最长链$f[x]$和$g[x]$

这样就是对一个割集内的每条边$(x,y)$求$f[x] + g[y]$的最大值

我们另所有点都在$T$集,$S$在$S$集

每次从$T$删一个点加入$S$

删除的时候删掉它的入边 记录答案 然后加入它的出边

具体 我们要加入一个元素 删除一个元素 查询最大值

这个用堆或者线段树都可以

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
int n,m;
const int maxn = 500010;
struct Graph
{
    int first[maxn],to[maxn << 1],nx[maxn << 1],cnt;
    int ind[maxn],indd[maxn];
    inline void add(int u,int v){to[++cnt] = v;nx[cnt] = first[u];first[u] = cnt;ind[v]++;indd[v]++;}
}G,rev;
priority_queue<int> q1,q2;
queue<int> q;
int dp[maxn],f[maxn];
inline void push(int x){q1.push(x);}
inline void del(int x){q2.push(x);while(!q1.empty() && !q2.empty() && (q1.top() == q2.top()))q1.pop(),q2.pop();}
inline int top(){if(q1.empty())return 0;return q1.top();}
void toposort()
{
    for(int i=1;i<=n;i++)
        if(G.ind[i] == 0)q.push(i);
    while(!q.empty())
    {
        int now = q.front();q.pop();
        for(int i=G.first[now];i;i=G.nx[i])
        {
            G.ind[G.to[i]]--;
            dp[G.to[i]] = max(dp[G.to[i]],dp[now] + 1);
            if(G.ind[G.to[i]] == 0)q.push(G.to[i]);
        }
    }
    while(!q.empty())q.pop();
    for(int i=1;i<=n;i++)
        if(rev.ind[i] == 0)q.push(i);
    while(!q.empty())
    {
        int now = q.front();q.pop();
        for(int i=rev.first[now];i;i=rev.nx[i])
        {
            rev.ind[rev.to[i]]--;
            f[rev.to[i]] = max(f[rev.to[i]],f[now] + 1);
            if(rev.ind[rev.to[i]] == 0)q.push(rev.to[i]);
        }
    }
}
void solve()
{
    int ans = (n << 1),id;
    while(!q.empty())q.pop();
    for(int i=1;i<=n;i++)
        if(G.indd[i] == 0)q.push(i);
    while(!q.empty())
    {
        int now = q.front();q.pop();
        del(f[now]);
        for(int i=rev.first[now];i;i=rev.nx[i])del(dp[rev.to[i]] + f[now] + 1);
        if(top() < ans)
        {
            ans = top();
            id = now;
        }
        for(int i=G.first[now];i;i=G.nx[i])
        {
            push(f[G.to[i]] + dp[now] + 1);
            G.indd[G.to[i]]--;
            if(G.indd[G.to[i]] == 0)q.push(G.to[i]);
        }
        push(dp[now]);
    }
    printf("%d %d",id,ans);
}
int main()
{
    n = read();m = read();
    for(int i=1;i<=m;i++){int u = read(),v = read();G.add(u,v);rev.add(v,u);}
    toposort();
    for(int i=1;i<=n;i++)push(f[i]);
    solve();
     
}
View Code

 

posted @ 2018-08-23 23:00  探险家Mr.H  阅读(176)  评论(0编辑  收藏  举报