2019雅礼集训 D4T1 w [费用流]

题目描述:

样例:

input1:
4 1 2
1 2 3 4
1 2
1 3
3 4
1 2
2 3
1 4
2
1 3
4 1
1
2 3

output1:
9

input2:
5 1 1
3 99 99 100 2
1 2
1 3
3 4
3 5
1 3
1 2
2 4
2 5
2
1 2
3 1
2
1 2
2 1

output2:
198

数据范围:

标签:费用流


先放个原题地址:CF1061E

毒瘤出题人搬原题差评

毒瘤出题人题目翻译出锅差评

这题看到如此不伦不类的问法,似乎不是dp、贪心等算法,而且数据范围有如此之小,于是自然(个鬼)想到了费用流。

虽然如此,我由于做的题太少,赛场上仍然想不出如何建模。(其实赛场上就没想到费用流)

注意到题目保证根节点一定会有限制,我们记有限制的点为关键点,不关键的点的贡献可以算在关键点上。

可以想到,每个关键点\(p\)实际能够操控的点\(x\)满足:\(p\)\(x\)的第一个关键祖先,记为\(id_x=p\)

我们再记每个关键点\(p\)能操控的\(x\)中能被激活的点的总数为\(sum_p\),即\(p\)的子树内可选节点减去其他关键点的可选节点。(怎么越说越复杂……)

那么:

一、源点向每个红树的关键点\(p\)连一条流量为\(sum_p\),费用为0的边。
二、每个蓝树的关键点\(p\)向汇点连一条流量为\(sum_p\),费用为0的边。
三、\(1\)\(n\)的每个点\(x\)红树和蓝树分别有一个关键祖先\(p\),\(p'\)。由\(p\)\(p'\)连一条流量为1,费用为\(value_x\)的边。

第三点的边走了就代表选了这个点,否则就是不选这个点。

最后跑一边最大费用最大流即可。

判无解的方法:一棵树内有关键点的可选节点为负数(即自相矛盾,许多人被出题人坑死在这个点上),最大流不能跑满或两棵树总共选的节点数不一样。

代码:

#include<bits/stdc++.h>
namespace my_std{
    using namespace std;
    #define mod 998244353
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define sz 500500
    typedef long long ll;
    template<typename T>
    inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();
        double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.')
        {
            ch=getchar();
            while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();
        }
        t=(f?-t:t);
    }
    template<typename T,typename... Args>
    inline void read(T& t,Args&... args){read(t); read(args...);}
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.txt","r",stdin);
        #endif
    }
    inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n;

namespace tree
{

    int rt0,rt1;
    struct hh{int t,nxt;}edge[sz];
    int head[sz],ecnt;
    void make_edge(int f,int t)
    {
        edge[++ecnt]=(hh){t,head[f]};
        head[f]=ecnt;
        edge[++ecnt]=(hh){f,head[t]};
        head[t]=ecnt;
    }
    
    int w[sz],a[sz],id[sz],sum[sz],T;
    void dfs(int x,int fa)
    {
        if (a[x])
        {
            sum[id[x]=++T]=a[x];
            if (fa) sum[id[fa]]-=a[x];
            if (fa&&sum[id[fa]]<0) puts("-1"),exit(0);
        }
        else id[x]=id[fa];
        #define v edge[i].t
        go(x) if (v!=fa) dfs(v,x);
        #undef v
    }
    
    void init()
    {
        read(rt0,rt1);rt1+=n;
        int m,x,y;
        rep(i,1,n) read(w[i]);
        rep(i,1,n-1) read(x,y),make_edge(x,y);
        rep(i,1,n-1) read(x,y),make_edge(x+n,y+n);
        read(m);
        rep(i,1,m) read(x,y),a[x]=y;
        read(m);
        rep(i,1,m) read(x,y),a[x+n]=y;
        dfs(rt0,0);dfs(rt1,0);
    }
    
}
using tree::a;using tree::w;using tree::id;using tree::sum;

struct hh{int t,w,dis,nxt;}edge[sz];
int head[sz],ecnt=1;
void make_edge(int f,int t,int w,int dis)
{
    edge[++ecnt]=(hh){t,w,dis,head[f]};
    head[f]=ecnt;
    edge[++ecnt]=(hh){f,0,-dis,head[t]};
    head[t]=ecnt;
}

int pre[sz],dis[sz],flow[sz];
bool in[sz];
int S,T;
bool SPFA()
{
    queue<int>q;
    memset(dis,~0x3f,sizeof(dis));
    q.push(S);
    dis[S]=0;in[S]=1;
    flow[S]=INT_MAX;
    while (!q.empty())
    {
        int x=q.front();q.pop();in[x]=0;
        #define v edge[i].t
        go(x) if (dis[v]<dis[x]+edge[i].dis&&edge[i].w>0)
        {
            dis[v]=dis[x]+edge[i].dis;
            pre[v]=i;
            flow[v]=min(flow[x],edge[i].w);
            if (!in[v]) q.push(v);
            in[v]=1;
        }
        #undef v
    }
    return dis[T]!=dis[0];
}
int L,R;
void build()
{
    S=n*2+1;T=S+1;
    rep(i,1,n) if (a[i]) {make_edge(S,id[i],sum[id[i]],0);L+=sum[id[i]];}
    rep(i,n+1,n<<1) if (a[i]) {make_edge(id[i],T,sum[id[i]],0);R+=sum[id[i]];}
    rep(i,1,n) make_edge(id[i],id[i+n],1,w[i]);
}
int ans,mxflow;
void update(){mxflow+=flow[T];ans+=flow[T]*dis[T];for (int x=T,y;(y=pre[x],x!=S);x=edge[y^1].t) edge[y].w-=flow[T],edge[y^1].w+=flow[T];}
void MCF(){build();while (SPFA()) update();}
int main()
{
    file();
    read(n);
    tree::init();
    MCF();
    printf("%d",L==R&&L==mxflow?ans:-1);
}

猜你喜欢

转载自www.cnblogs.com/p-b-p-b/p/10246386.html