前缀和思想

92 篇文章 0 订阅
86 篇文章 0 订阅

一个区间可以拆成两个前缀和,这是一个很基本的思想。
反过来,两个前缀和的关系可以由一个区间表示。。。
有时某些区间问题可以转化为前缀和问题继而减少情况和状态。。
例:
魔术师的桌子上有n个杯子排成一行,编号为1,2,…,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品。花费c_ij元,魔术师就会告诉你杯子i,i+1,…,j底下藏有球的总数的奇偶性。
采取最优的询问策略,你至少需要花费多少元,才能保证猜出哪些杯子底下藏着球?
n<=2000

可以发现,第i个杯下球是否存在取决于前缀i与前缀i-1的关系,反之如果知道第i个杯下球是否存在那么可以知道前缀i和前缀i-1的关系,意思是这两个问题是等价的!
那么我们就需要知道对于每个i,前缀i和前缀i-1的关系。
如果我们询问了区间[l,r],那么我们就知道了前缀l-1和前缀r的关系,把每个前缀看做一个点,两个点之间的关系当做边,那么我们需要把0~n这n+1个点联通。(关系是可以传递的)
那么我们就求一个最小生成树就行了。

AC Code:

#include<bits/stdc++.h>
#define maxn 2005
#define LL long long
using namespace std;

int n,c[maxn][maxn],val[maxn];
bool usd[maxn];
LL ans;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            scanf("%d",&c[i-1][j]) ,  c[j][i-1] = c[i-1][j];

    memset(val,0x3f,sizeof val);
    val[0] = 0;
    for(int i=0;i<=n;i++)
    {
        int Min = -1;
        for(int j=0;j<=n;j++)
            if(!usd[j] && (Min == -1 || val[j]<val[Min])) Min = j;
        usd[Min] = 1;
        ans += val[Min];
        for(int j=0;j<=n;j++)
            if(!usd[j] && val[j] > c[Min][j])
                val[j] = c[Min][j];
    }

    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值