专项测试2


A. 收藏家

网络流,把每个人都拆点每个时间拆成一个,之间连 \(a_i\) 容量的边,保证每个之间最多有 \(a_i\) 个物品

再从 \(S\) 向每个人连 \(1\) 的边,因为最后要求的是不一样的物品数量,每有一个流量就代表有一个不同的物品

第一个人再向 \(T\)\(a_1\) 的边

每个时间都有把交换的两个人互相连 \(1\) 的边权,因为有方向限制,所以时间靠后的不会影响前面的

最后发现没用的点很多,于是只把有用的点建出来跑网络流就行

Code
#include
//#define int long long
#define rint signed
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int tests;
int n,m,T,S,cnt;
int a[3010],lst;
int pre[3010];
int head[6010],HD[6010],ver[40010],to[40010],edge[40010],tot;
int q[6010],h,t,dis[6010];
inline void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;to[tot]=head[x];head[x]=tot;
	ver[++tot]=x;edge[tot]=0;to[tot]=head[y];head[y]=tot;
}
inline bool bfs(){
	for(int i=1;i<=cnt;i++) dis[i]=inf;
	q[h=t=1]=S,dis[S]=0;memcpy(head,HD,sizeof(head));
	while(h<=t){
		int x=q[h++];
		for(int i=head[x];i;i=to[i]) if(edge[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+1) dis[y]=dis[x]+1,q[++t]=y;
		}
		if(x==T) return true;
	}
	return false;
}
int dfs(int x,int in){
	if(x==T) return in;
	int res=in,go;
	for(int i=head[x];i;head[x]=i=to[i]) if(edge[i]){
		int y=ver[i];
		if(dis[y]==dis[x]+1){
			go=dfs(y,min(res,edge[i]));
			if(go) edge[i]-=go,edge[i^1]+=go,res-=go;
			else dis[y]=0;
		}
		if(!res) break;
	}
	return in-res;
}
inline void solve(){
	int res=0;lst=0;cnt=2;S=1,T=2;
	n=read(),m=read();tot=1;
	memset(head,0,sizeof(head));
	memset(pre,0,sizeof(pre));
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1,x,y;i<=m;i++){
		x=read(),y=read();
		if(pre[x]){add(pre[x],++cnt,a[x]);pre[x]=cnt;}
		else{add(S,++cnt,1);pre[x]=cnt;}
		if(pre[y]){add(pre[y],++cnt,a[y]);pre[y]=cnt;}
		else{add(S,++cnt,1);pre[y]=cnt;}
		add(pre[x],pre[y],1);
		add(pre[y],pre[x],1);
	}
	add(pre[1],T,a[1]);
	memcpy(HD,head,sizeof(head));
	while(bfs()) res+=dfs(S,inf);
	printf("%d\n",res);
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	tests=read();while(tests--) solve();
	return 0;
}

B. 旅行

朴素的 \(dp\) 方程时 \(f_u=\frac{\sum f_v}{deg_u}+1\) ,要最大化取值

前面的部分可以用 01分数规划二分来做

设当前的二分答案为 \(mid\)

\[\frac{\sum f_v}{deg_u}>mid \]

\[\sum f_v>mid\times deg_u \]

\[\sum f_v-mid\times deg_u>0 \]

那么每个选择的边贡献 \(f_v-mid\)

删边的限制为删 \(y\) 必须删 \(x\) 的限制可以变成选择 \(x\) 则必须选 \(y\)

可以用网络流跑最大权闭合子图来解决

Code
#include
//#define int long long
#define eps 1e-4
#define rint signed
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,k,S,T;
int head[510],HD[510],ver[10010],to[10010],tot;
double edge[10010];
int dis[510],q[510],h,t;
int deg[60];
double f[60];
vectorg[60],gg[60];
queueque;
struct E{int x,y;}e[510],ee[2010];
inline void add(int x,int y,double z){
	//printf("%lld -> %lld : %.10lf\n",x,y,z);
	ver[++tot]=y;edge[tot]=z;to[tot]=head[x];head[x]=tot;
	ver[++tot]=x;edge[tot]=0;to[tot]=head[y];head[y]=tot;
}
inline void build(){
	memset(head,0,sizeof(head));tot=1;
	for(int i=1;i<=k;i++) if(ee[i].x!=ee[i].y) add(ee[i].x,ee[i].y,inf);
}
inline bool bfs(){
	for(int i=1;i<=T;i++) dis[i]=inf;
	q[h=t=1]=S,dis[S]=0;memcpy(head,HD,sizeof(head));
	while(h<=t){
		int x=q[h++];
		for(int i=head[x];i;i=to[i]) if(edge[i]>eps){
			int y=ver[i];
			if(dis[y]>dis[x]+1) dis[y]=dis[x]+1,q[++t]=y;
		}
		if(x==T) return true;
	}
	return false;
}
double dfs(int x,double in){
	if(x==T) return in;
	double res=in,go;
	for(int i=head[x];i;head[x]=i=to[i]) if(edge[i]>eps){
		int y=ver[i];
		if(dis[y]==dis[x]+1){
			go=dfs(y,min(res,edge[i]));
			if(go) edge[i]-=go,edge[i^1]+=go,res-=go;
			else dis[y]=0;
		}
		if(!res) break;
	}
	return in-res;
}
inline bool check(int x,double mid){
	double sum=0,res;
	build();
	for(int i=1;i<=m;i++) if(e[i].x==x){
		res=f[e[i].y]-mid;
		if(res>eps) sum+=res,add(S,i,res);
		else if(reseps;
}
inline double calc(int x){
	double l=0,r=100;
	while(r-l>eps){
		double mid=(l+r)/2.0;
		if(check(x,mid)) l=mid;
		else r=mid;
	}
	return l;
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	n=read(),m=read(),k=read();S=m+1,T=m+2;
	for(int i=1;i<=m;i++) e[i].x=read(),e[i].y=read();
	for(int i=1;i<=k;i++) ee[i].x=read(),ee[i].y=read();
	for(int i=1;i<=m;i++){
		g[e[i].y].emplace_back(e[i].x);
		gg[e[i].x].emplace_back(ee[i].y);
		deg[e[i].x]++;
	}
	for(int i=1;i<=n;i++) if(!deg[i]) que.push(i);
	while(!que.empty()){
		int x=que.front();que.pop();
		if(gg[x].size()) f[x]=calc(x)+1;
		else f[x]=0;
		for(auto y:g[x]){
			deg[y]--;
			if(!deg[y]) que.push(y);
		}
	}
	printf("%.10lf\n",f[1]);
	return 0;
}