[BZOJ]3714: [PA2014]Kuglarz
题解 orz最小生成树
查询[l,r]区间需要花费Cij 那么我们假设有n+1个状态分别表示前缀查询 那么我们对于查询[i,j]等价于(-1,j)连边 因为我们可以在Cij花费下 实现两个前缀状态的转移 又已知 我们知道所有前缀状态时 可以轻易找出每个位置的状态 所以只需要连边求MST即可
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=2e6+10; const double eps=1e-8; #define ll long long using namespace std; struct edge{int t,v;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; void add(int x,int y,int vul){o->t=y;o->v=vul;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } typedef struct node{ int u,v,k; friend bool operator<(node aa,node bb){return aa.k<bb.k;} }node; node d[MAXN]; int f[2005]; int find1(int x){ if(x==f[x])return x; return f[x]=find1(f[x]); } int main(){ int n=read(); int cnt=0,k; inc(i,1,n){ inc(j,i,n){ d[++cnt]=(node){i,j+1,k=read()}; } } sort(d+1,d+cnt+1); ll ans=0; inc(i,1,n+1)f[i]=i; inc(i,1,cnt){ int t1=find1(d[i].u);int t2=find1(d[i].v); if(t1==t2)continue; f[t1]=t2;ans+=d[i].k; } printf("%lld\n",ans); }
3714: [PA2014]Kuglarz
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2224 Solved: 1029
[Submit][Status][Discuss]
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
1 2 3 4 5
4 3 2 1
3 4 5
2 1
5
1 2 3 4 5
4 3 2 1
3 4 5
2 1
5
Sample Output
7