题意:给定n和k,n表示n个房间,k表示wifi的传播范围,同时给定一个字符串,表示可以放置路由器的房间,如果可以放置路由器,那么max(1, n - k)
和min(n, n + k)范围里的房间都可以得到信号,从而可以不直接联网,每个房间联网的代价为它的下标i。

分析:
我们假设f[i][0 / 1]表示第i号房间不联网/联网的最小代价。
如果联网了,并且这个房间里面由wifi,那就更好,可以从更前面的状态转移过来,否则就从前面一个房间转移过来。

如果f[i][0] (表示第i个房间不联网),显然它需要从前面有wifi的地方转移过来,
如果f[i][1] (表示第i个房间联网),那么它有wifi的话,可以从更前面转移过来,如果没有wifi,那就从前面一个转移过来。

但是这样做的时间复杂度为o(n * k)

不优化的代码,TLE在23个点

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const int N = 200005;
 
char s[N];
int f[N][2];
 
int main()
{
	int n, k;
	scanf("%d%d", &n, &k);
	scanf("%s", s + 1);
 
	memset(f, 0x3f, sizeof f);
	f[0][0] = 0, f[0][1] = 0;
	for (int i = 1; i <= n; ++i)
	{
		if (s[i] == '0')
		f[i][1] = min(f[i][1], min(f[i - 1][0], f[i - 1][1]) + i);
		for (int j = 0; j <= i - 1; ++j)
		{
			if (s[j] == '1' && j + k >= i)
				f[i][0] = min(f[i][0], f[j][1]);
			if (s[i] == '1' && j >= i - k - 1)
				f[i][1] = min(f[i][1], min(f[j][0], f[j][1]) + i);
		}
	}
 
	printf("%d\n", min(f[n][1], f[n][0]));
 
 
 
	return 0;
}

通过线段树维护区间最小值的代码
这里用了三棵线段树,因为我需要从前面有wifi并且状态是联网的房间转移过来,所以不得已再维护一个线段树。
因为我的状态定义和其它博客的不一样,其它博客表示f[i][0/1]这个房间有没有路由,但是这样我觉的分不清楚。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
using LL = long long;
const int N = 200005;
const LL inf = 1e18;
char s[N];
LL f[N][2];
//0:不买 1:买
 
struct ST
{
	int l, r;
	LL val;
};
//tr维护不买的,tr2维护买的
ST tr[N * 4], tr2[N * 4], tr3[N * 4];//三棵线段树
 
void pushup(ST tr[], int u)
{
	tr[u].val = min(tr[u].val, tr[u << 1].val);
	tr[u].val = min(tr[u].val, tr[u << 1 | 1].val);
}
 
void build(ST tr[], int u, int l, int r)
{
	tr[u].l = l, tr[u].r = r, tr[u].val = inf;
	if (l == r)
	{
		//tr[u].val = inf;
		return;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	build(tr, u << 1, l, mid), build(tr, u << 1 | 1, mid + 1, r);
	//pushup(tr, u);
}
 
//单点修改
void modify(ST tr[], int u, int pos, LL v)
{
	if (tr[u].l == tr[u].r)
	{
		tr[u].val = v;
		return;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	if (pos <= mid) modify(tr, u << 1, pos, v);
	else if (pos > mid) modify(tr, u << 1 | 1, pos, v);
	pushup(tr, u);
}
 
//区间查询最小值
 
LL query(ST tr[], int u, int l, int r)
{
	if (l <= tr[u].l && r >= tr[u].r)
	{
		return tr[u].val;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	LL res = inf;
	if (l <= mid) res = min(res, query(tr, u << 1, l, r));
	if (r > mid) res = min(res, query(tr, u << 1 | 1, l, r));
	return res;
}
 
 
int main()
{
	int n, k;
	scanf("%d%d", &n, &k);
	scanf("%s", s + 1);
 
	build(tr, 1, 0, n);
	build(tr2, 1, 0, n);
	build(tr3, 1, 0, n);
 
	for (int i = 1; i <= n; ++i)
		f[i][0] = f[i][1] = inf;
	modify(tr, 1, 0, 0);//f[i][0]
	modify(tr2, 1, 0, 0);//f[i][1]
	f[0][0] = 0, f[0][1] = 0;
	for (int i = 1; i <= n; ++i)
	{
		//没有路由,并且购买了的
		if (s[i] == '0')
		{
			LL q = min(f[i - 1][0], f[i - 1][1]);
			if (f[i][1] > q + i)
			{
				f[i][1] = q + i;
				modify(tr2, 1, i, f[i][1]);
			}
		}
		//没有购买,由前面的wifi提供
		LL res = query(tr3, 1, max(1, i - k), i - 1);
		if (f[i][0] > res)
		{
			f[i][0] = res;
			modify(tr, 1, i, f[i][0]);
		}
		res = inf;
		res = min(res, query(tr, 1, max(0, i - k - 1), i - 1));
		res = min(res, query(tr2, 1, max(0, i - k - 1), i - 1));
		if (s[i] == '1')
		{
			f[i][1] = res + i;
			modify(tr2, 1, i, f[i][1]);
			modify(tr3, 1, i, f[i][1]);
		}		
	}
 
	printf("%lld\n", min(f[n][1], f[n][0]));
 
 
 
	return 0;
}