[BZOJ3714]Kuglarz(图论+最小生成树)

BZOJ3714

Description

魔术师的桌子上有n个杯子排成一行,编号为1,2,…,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品。花费c_ij元,魔术师就会告诉你杯子i,i+1,…,j底下藏有球的总数的奇偶性。
采取最优的询问策略,你至少需要花费多少元,才能保证猜出哪些杯子底下藏着球?

Input

第一行一个整数n(1<=n<=2000)。
第i+1行(1<=i<=n)有n+1-i个整数,表示每一种询问所需的花费。其中c_ij(对区间[i,j]进行询问的费用,1<=i<=j<=n,1<=c_ij<=10^9)为第i+1行第j+1-i个数。

Output

输出一个整数,表示最少花费。

Sample Input

5

Sample Output

7

题意

每次可以花费\(c_{i,j}\)来询问\(f_{i,j}\)

其中

\[f_{i,j}=\left(\sum_{x=i}^js_x\right)\&1 \]

求最小花费还原\(s\)

分析

我们可以发现,如果没有按位与1,那么这个\(f\)就是一个前缀和。按位与限制了他不能进位,那么其实就是二进制下不进位的加法——异或。

所以\(f_{i,j}=f_{1,i-1}\oplus f_{1,j}\)就是一个异或前缀和,也可以写成这样\(f_{1,i-1}\oplus f_{i,j}=f_{1,j}\)

然后再考虑会按位与,虽然他有按位与,但是\(s\)的取值也是01,所以\(i=j\)时我们就可以用\(f_{i,j}=f_{1,j}\oplus f_{1,i-1}\)来表示\(s_i\)

那么我们要还原\(s\)就是要知道所有的\(f_{i,i}\),退一步说,就是要知道所有的\(f_{1,i}\)

我们需要用最小的花费来求出\(f_{1,i}\),这个值除了直接询问之外,还可以用前缀和的性质拼出来。

\[f_{1,j}=f_{1,i-1}\oplus f_{i,j}\quad (1<i<j\leq n) \]

那么我们就可以把这个问题抽象成图论:

我们用询问\([i,j]\)的信息,得到\(f_{i,j}\),实际上就是走了一条\((s=i-1,t=j,w=c_{i,j})\)的边。

那么我们从\(0\)出发,走到点\(j\),的最短路就是我得知\(f_{1,j}\)的最小花费。

现在我要到达所有点,求最小花费,那么就是最小生成树了。

考虑到边数远远大于点数,考虑使用贪点的方法跑最小生成树。

总结

  1. \(\forall 1<i\leq j \leq n\),建立一条\((s=i-1,t=j,w=c_{i,j]})\)的无向边
  2. \(0\)为根,跑最小生成树。
  3. 最小生成树的边权和就是答案
#include <cstdio>
#include <cstring>
#include <queue>
#include <utility>

long long unsigned n,tmp;
struct Edge
{
	long long unsigned u,v,w;
}edge[8000021];
long long unsigned head[2010];
inline void add(long long unsigned a,long long unsigned b,long long unsigned c)
{
	static long long unsigned tot=0;
	edge[++tot]=(Edge){head[a],b,c}; head[a]=tot;
	edge[++tot]=(Edge){head[b],a,c}; head[b]=tot;
}

long long unsigned mst()
{
	long long unsigned dis[2011],res=0;
	bool vis[2011];
	std::memset(vis,0x00,sizeof(vis));
	std::memset(dis,0x7f,sizeof(dis));
	std::priority_queue<long long unsigned,std::vector<std::pair<long long unsigned,long long unsigned> >,std::greater<std::pair<long long unsigned,long long unsigned> > > q;
	q.push(std::make_pair(0,0));
	while(not q.empty())
	{
		long long unsigned i=q.top().second, tmp=q.top().first; q.pop(); if(vis[i]) continue;
		vis[i]=true; res+=tmp;
		for(register long long unsigned j=head[i];j;j=edge[j].u)
		{
			if(dis[edge[j].v]>edge[j].w) q.push(std::make_pair(dis[edge[j].v]=edge[j].w,edge[j].v));
		}
	}
	return res;
}

int main()
{
	scanf("%llu",&n);
	for(register long long unsigned i=1;i<=n;++i)
	{
		for(register long long unsigned j=i;j<=n;++j)
		{
			scanf("%llu",&tmp);
			add(i-1,j,tmp);
		}
	}
	printf("%llu\n",mst());
	return 0;
}
posted @ 2022-01-24 14:45  IdanSuce  阅读(48)  评论(0编辑  收藏  举报