[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 MB
Submit: 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

Sample Output

7
posted @ 2019-02-02 00:35  wang9897  阅读(132)  评论(0编辑  收藏  举报