首页 > 其他分享 >SP839Optimal Marks 题解

SP839Optimal Marks 题解

时间:2024-01-17 15:35:35浏览次数:27  
标签:head int 题解 Marks cnt 最小 SP839Optimal 赋值 dis

part1:建图

二进制异或,每一位互不干扰。所以对每一位分开来考虑。

然后变成了一个经典的模型。

当前每一个未确定点有两个选择:变成 \(1\),变成 \(0\);已经确定的点只能选它本身的值。

于是构造思路非常套路了:构造虚点 \(S\)、\(T\)。对于一个点 \(u\),从 \(S\) 连向 \(u\) 一条边,值为 \(u\) 不取 \(1\) 的代价。从 \(u\) 连向 \(T\) 一条边,表示 \(u\) 不取 \(0\) 的代价。对于原图上的一条边 \((u,v)\),在网络流的图上 \(u,v\) 间连一条值为 \(1\) 的边。图的最小割就是当前这个二进制位会异或出多少个 \(1\)。

上图是一个例子。\(A\) 点已给出,在当前位上为 \(1\),\(B\) 点已给出,在当前位上为 \(0\)。\(A,C\) 在原图上有一条边。

\(A\) 不能不取 \(1\),代价为 \(inf\),\(B\) 不能不取 \(0\),代价为 \(inf\)。\(C\) 没有限制,所以两边的代价都是 \(0\).

part2:优化

图中为 \(0\) 的边显然对跑网络流没有影响,可以全部删掉。这个时候,如果一个点 \(u\) 没有初始被赋值,也没有与任何点连边。它就没有与任何边了呀?我怎么知道它取什么值呢?其实这种点你全部给它赋值为 \(0\) 就可以了。

part3:求方案数与一些解释

复习一下最大流怎么对应上最小割?在最终的残留网络上,从 \(S\) 开始 bfs,只走那些不为 \(0\) 的边,能走到的点都划分为 \(S\) 部。

所以在这道题中,在残留网络上从 \(S\) 开始 bfs能走到的点都被我们赋值为 \(1\),其它点都赋值为 \(0\) 就可以了。

但是还有一个问题,题目中要求 在边权和最小的条件下,点权和最小的方案,上面的做法好像没有保证点权和最小呀?其实在 part2 中,把所有“孤立点”赋值为 \(0\) 已经保证了点权和最小。在优化后的网络流图上,找到一种最小割方案后,你是没有办法调整割的边使点权和更小的。你可以自己画画图稍微思考一下。

part4:代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=505,MAXM=1e5+5;
struct E{int nxt,to,w;}e[MAXM];
int cnt=1;
int head[MAXN],cur[MAXN];
void add(int u,int v,int w){
	// printf("%d %d %d\n",u,v,w);
	e[++cnt]={head[u],v,w};head[u]=cnt;
	e[++cnt]={head[v],u,0};head[v]=cnt;
}
int dis[MAXN];
const int INF=0x3f3f3f3f;
int n,S,T;
bool bfs(){
	for(int i=1;i<=n;i++)	dis[i]=INF;
	dis[S]=0;
	queue<int> q;q.push(S);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(e[i].w&&dis[v]==INF){
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return dis[T]<INF;
}
int dfs(int u,int limit){
	if(u==T)	return limit;
	int ret=0;
	for(int i=cur[u];i;i=e[i].nxt){
		int v=e[i].to;
		cur[u]=i;
		if(e[i].w&&dis[v]==dis[u]+1){
			int add=dfs(v,min(limit-ret,e[i].w));
			if(!add)	dis[v]=-1;
			ret+=add,e[i].w-=add,e[i^1].w+=add;
			if(ret==limit)	return ret;
		}
	}
	return ret;
}
int dinic(){
	int ans=0;
	while(bfs()){
		for(int i=1;i<=n;i++)	cur[i]=head[i];
		ans+=dfs(S,INF);
	}
	return ans;
}
int N,M;
ll a[505],b[505];
int u[3005],v[3005];
bool vis[MAXN];
void find(int u,ll dlt){
	vis[u]=1;
	if(u!=S)	b[u]+=dlt;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(e[i].w&&!vis[v])	find(v,dlt);
	}
}
int main(){
// 	freopen(".in","r",stdin);
// 	freopen(".out","w",stdout);
	int wwh;scanf("%d",&wwh);
	while(wwh--){
		scanf("%d%d",&N,&M);
		for(int i=1;i<=M;i++)	scanf("%d%d",&u[i],&v[i]);
		for(int i=1;i<=N;i++)	a[i]=-1,b[i]=0;
		int K;scanf("%d",&K);
		for(int i=1;i<=K;i++){
			int x;scanf("%d",&x);
			scanf("%lld",&a[x]);
		}
		n=N+2;S=n-1,T=n;
		ll ans=0;
		for(int i=30;i>=0;i--){
			for(int i=1;i<=n;i++)	head[i]=0;
			cnt=1;
			for(int j=1;j<=N;j++){
				if(a[j]==-1)	continue;
				else{
					int op=((a[j]>>i)&1);
					op==0?add(j,T,INF):add(S,j,INF);
				}
			}
			for(int j=1;j<=M;j++)	add(u[j],v[j],1),add(v[j],u[j],1);
			int add=dinic();
			for(int j=1;j<=n;j++)	vis[j]=0;
			find(S,(1ll<<i));
			ans+=1ll*add*(1ll<<i);
		}
		for(int i=1;i<=N;i++){
			printf("%lld\n",b[i]);
		}
	}
	return 0;
}

标签:head,int,题解,Marks,cnt,最小,SP839Optimal,赋值,dis
From: https://www.cnblogs.com/bwartist/p/17970126

相关文章

  • ABC311_g One More Grid Task 题解
    题目链接:Atcoder或者洛谷对于解决二维区间内的最值类型问题,我们常常有一类特别好用的方法,就是悬线法,它可以看做是单调栈的子集,但更加好理解和书写。对于悬线法,我们有一个常见的模型,找出面积最大的符合题意的最大的矩形:例题P4147玉蟾宫。对于悬线法而言,我们需要理解什么是悬......
  • P2216 [HAOI2007] 理想的正方形 题解
    题目链接:理想的正方形比较明显的,我们可以用二维ST表解决,具体的二维ST表的实现,只需要知道一点:对于\(st[i][j][t]=max(i\simi+2^t,j\simj+2^t)\),表示的是如图所示的大正方形范围内的最值,它可以拆成从四个小正方形的左端点走\(2^{t-1}\)长的小正方形组成,预处理完直接查......
  • 【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(上)
    知识盲点概念介绍HashMap是基于Map接口构建的数据结构,它以键值对的形式存储元素,允许键和值都为null。由于键的唯一性,HashMap中只能有一个键为null。HashMap的特点是元素的无序性和不重复性。注意,HashMap并不是线程安全的。在多线程环境下,如果不进行适当的同步处理,可能会导致数据不......
  • P9018 [USACO23JAN] Moo Route G 题解
    首先有一些性质。因为保证有解,所以\(a_i\)一定都是\(2\)的倍数(必须一来一回)。并且总的步数应该为\(\suma_i\)。先考虑\(n\le2\)的情况,这时我们可以分情况讨论。因为每一条线段都会被来回走两次,所以我们可以先把每一个数都除以\(2\)。若\(a=b\),则最优情况一定是形......
  • P9017 [USACO23JAN] Lights Off G 题解
    一次操作相当于把\(a\)异或上\(b\),修改开关的一位相当于将这一位异或上\(1\)。会发现一个很神奇的性质:初始开关对灯的影响和改变开关状态对灯的影响是独立的。而前者的影响是固定的,所以我们可以只考虑改变开关状态对灯的影响。假设一共需要\(k\)次操作能使所有灯关闭,如果我......
  • CF1876D Lexichromatography 题解
    Problem-D-CodeforcesLexichromatography-洛谷先注意读题:对于所有的值\(k\),在这个序列的任意子区间\([l,r]\)中,值为\(k\)且为红色的位置数减去值为\(k\)且为蓝色的位置数的绝对值不超过\(1\)注意是任意子区间这说明什么?说明如果只有第二个条件,我......
  • P2572 [SCOI2010] 序列操作 题解
    题解:序列操作比较综合的ds题,综合了线段树常见的几种操作:维护最大子段和、区间翻转、区间求和、区间覆盖。维护子段和常见的我们维护三类东西:前缀最长连续段、后缀最长连续段、当前区间上的最大子段和。在pushUp时,对于一个区间的前后缀最值首先等于左右子树的最长前后缀,......
  • Flink自定义Assigning Timestamps和Watermarks 使用Scal语言
    Flink自定义AssigningTimestamps和Watermarks使用Scal语言为了让eventtime工作,Flink需要知道事件的时间戳,这意味着流中的每个元素都需要分配其事件时间戳。这个通常是通过抽取或者访问事件中某些字段的时间戳来获取的。时间戳的分配伴随着水印的生成,告诉系统事件时间中的......
  • P6054 题解
    blog。网络流——最小割。每个选手做某一套题的期望奖励固定,计算方式参考样例解释。这个假期望被去掉了。发现是典型的「\(m\)种强制选一」问题。考虑每个人都建一条链,跑最小割,每条链必定割\(\ge1\)条边,割哪条边就表示选哪套题。code,时间复杂度\(O(\text{能过})\)。......
  • 洛谷P10058 题解
    这种翻转的题明显已经做烂了好吧……首先显而易见,翻转偶数次对结果没有影响,只需要考虑奇数次翻转的情况。由于是整体移动的操作,可以抓住一个点来移动,然后还原出原来的序列。需要注意的是字符串是环形移动,因此如果当前点的位置大于字符串长度,要对字符串的长度进行取余操作。写......