之前一个网友问我这题,当时我不会,可惜我不记得是谁问的了,非常抱歉。
考虑$x$购买,则$y$ 才可以满足$A$,就是在说,若$y$满足$A$,则推出$x$购买。
$x\rightarrow y$并且最大化权值和容易想到最大权闭合子图。
此外还可以关注到,物品$i$的价值$y$是关于$x$的二次函数,那么4段$[0,0],[L_i,L_i],(L_i,R_i),[R_i,R_i]$ 中,0看成没有选择,$(L_i,R_i)$看成一个点$i_1$,$L_i,R_i$看成一个点$i_2$。
$i_1$的点权定义为那三段的最大值,$i_2$的点权定义为$L_i,R_i$的最大值减去$i_1$点权,并令$i_2$向$i_1$连边,这样就给出物品$i$的4种情况(此时又选$i_1$又选$i_2$表示选$L_i$或$R_i$的最大值)。
剩下的连边就很简单了,对于第一种,$y_1$连向$x_1$,对于第二种$y_2$连向$x_1$
最后就是一个最大流。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<set>
typedef long long ll;
typedef std::vector<int> P;
typedef std::pair<int,int> pii;
ll read(){ll x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9')x=x*10+(c-'0'),c=getchar();return f*x;}
ll max(ll a,ll b){return a>b?a:b;}
ll min(ll a,ll b){return a<b?a:b;}
template <typename T> bool umax(T& a,T t){if(t>a)return a=t,1;return 0;}
template <typename T> bool umin(T& a,T t){if(t<a)return a=t,1;return 0;}
/**********/
const int MAXN = 200011;
const ll INF=1e18;
struct edge{int v,nxt;ll w;}e[MAXN<<2|1];
int cnt=1,last[MAXN],cur[MAXN];
void add_directed_edge(int u,int v,ll w){e[++cnt].v=v,e[cnt].w=w,e[cnt].nxt=last[u],last[u]=cnt;}
void adde(int u,int v,ll w){add_directed_edge(u,v,w),add_directed_edge(v,u,0);}
int dep[MAXN],Q[MAXN];
bool bfs(int s,int t,int n)
{
for(int i=1;i<=n;++i)dep[i]=0,cur[i]=last[i];
int qh=0,qt=0;
Q[qt++]=s,dep[s]=1;
while(qh<qt)
{
int u=Q[qh++];
for(int i=last[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(!dep[v]&&e[i].w)dep[v]=dep[u]+1,Q[qt++]=v;
}
}
return dep[t];
}
ll ex_flow(int u,int t,ll flow=INF)
{
if(u==t)return flow;
ll f=0;
for(int &i=cur[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(dep[v]==dep[u]+1&&e[i].w)
{
int tmp=ex_flow(v,t,min(e[i].w,flow-f));
e[i].w-=tmp,e[i^1].w+=tmp;
f+=tmp;
if(f==flow)return f;
}
}
return f;
}
ll Dinic(int s,int t,int n)
{
ll res=0;
while(bfs(s,t,n))res+=ex_flow(s,t);
return res;
}
ll A,B,C,val[MAXN];
inline ll f(ll x){return A*x*x+B*x+C;}
int main()
{
int n=read(),m=read(),S=2*n+1,T=S+1;
for(int i=1;i<=n;++i)
{
ll L=read(),R=read();
A=read(),B=read(),C=read();
ll f1=-INF,f2=max(f(L),f(R));
for(int j=L+1;j<R;++j)umax(f1,f(j));
val[i]=f1,val[n+i]=f2-f1;
adde(n+i,i,INF);
}
while(m--)
{
int t=read(),x=read(),y=read();
if(t==1)adde(y,x,INF);
else adde(n+y,x,INF);
}
ll ans=0;
for(int i=1;i<=n+n;++i)
if(val[i]>0)ans+=val[i],adde(S,i,val[i]);
else adde(i,T,-val[i]);
printf("%lld\n",ans-Dinic(S,T,T));
return 0;
}
请问cur[u]一定要随时更新吗?我的困惑在于
一,u在bfs某个儿子v时,v的后续dfs的点的dep一定大于dep[u],所以u不可能被ex_flow(v)访问
二,u在bfs所有儿子后,cur[u]必然为-1,也就是说可以直接在ex_flow(u)结束前,将cur[u]一步更新为-1.。
三,假设u第一次被访问到是作为v1的儿子,第二次被访问到是作为v2的儿子。由于被v1访问后,cur[u]已设置为-1,所以当v2再访问u时,必定无功而返。但事实上,u此时有可能还未被榨干,v2按理能够再次去榨干u,所以cur[u]没有必要在u被访问后设置为-1.。
综上所述,我认为cur没有设置的必要,或者第一次被访问后一次性设置为-1与第一次访问时随时更新的效果相同。
但实验证明,随时更新cur的用时500ms,一次性设置的用时为1300ms,不设置cur的用时为600ms,请问我的推理哪里有问题呢?
ex_flow
中如果当前流量已经用完了就立刻结束,就是if(f==flow)return f;
不一定用完所有儿子,所以你的二三都不对.事实上如果不这样做不能保证复杂度上界是$O(n^2m)$
感谢感谢
我很欣赏你这种探究和实验的精神啊!要向你学习