BZOJ3691 游行

Link
我们认为一条路径不覆盖它的起点,这样就能够消除第二条限制。
然后我们先把第三条限制抛开不管,要做的实际上就是求一个最小链覆盖。
每次我们会把一个点加入链覆盖,并且这个费用是单调递增的。
那么每次询问时二分找到费用小于\(C\)的最后一次覆盖,后面剩下的全部直接用\(C\)即可。

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int V=507,E=300007,inf=0x3f3f3f3f;
int s,t,tot=1,cnt,head[V],ver[E],next[E],edge[E],cost[E],dis[V],flow[V],inq[V],id[V],e[V][V],ans[E],sum[E];std::queue<int>q;
int read(){int x;scanf("%d",&x);return x;}
void add(int u,int v,int f,int c)
{
    ver[++tot]=v,next[tot]=head[u],head[u]=tot,edge[tot]=f,cost[tot]=c;
    ver[++tot]=u,next[tot]=head[v],head[v]=tot,edge[tot]=0,cost[tot]=-c;
}
int spfa()
{
    memset(dis+1,0x3f,t<<2),q.push(s),inq[s]=1,flow[s]=inf,dis[s]=0,id[t]=-1;
    for(int i,u,v;!q.empty();)
	for(i=head[u=q.front()],q.pop(),inq[u]=0;i;i=next[i])
	    if(edge[i]&&dis[v=ver[i]]>dis[u]+cost[i])
		if(dis[v]=dis[u]+cost[i],id[v]=i,flow[v]=std::min(flow[u],edge[i]),!inq[v])
		    q.push(v),inq[v]=1;
    return ~id[t];
}
void EK(){for(int p;spfa();) for(++cnt,sum[cnt]=sum[cnt-1]+(ans[cnt]=flow[t]*dis[t]),p=t;p^s;p=ver[id[p]^1]) edge[id[p]]-=flow[t],edge[id[p]^1]+=flow[t];}
int main()
{
    int n=read(),m=read(),q=read();
    memset(e,0x3f,sizeof e),s=n+n+1,t=s+1;
    for(int i=1,u,v,w;i<=m;++i) u=read(),v=read(),w=read(),e[u][v]=std::min(e[u][v],w);
    for(int i=1;i<=n;++i) add(s,i,1,0),add(i+n,t,1,0),add(i+n,i,inf,0);
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(e[i][j]^inf) add(i,j+n,inf,e[i][j]);
    EK();
    for(int x,p;q;--q) x=read(),p=std::upper_bound(ans+1,ans+cnt+1,x)-ans-1,printf("%d\n",sum[p]+x*(n-p));
}
posted @ 2020-02-29 19:42  Shiina_Mashiro  阅读(116)  评论(0编辑  收藏  举报