2019雅礼集训 D4T1 w [费用流]
Posted p-b-p-b
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
}
以上是关于2019雅礼集训 D4T1 w [费用流]的主要内容,如果未能解决你的问题,请参考以下文章