一个区间可以拆成两个前缀和,这是一个很基本的思想。
反过来,两个前缀和的关系可以由一个区间表示。。。
有时某些区间问题可以转化为前缀和问题继而减少情况和状态。。
例:
魔术师的桌子上有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);
}