如果dp方程写出来之后大概是长这样的 f [ i ] = ∑ 0 < j < i m i n ( f [ j ] + s [ i , j ] ) + … f[i]=\sum_{0<j<i} min(f[j]+s[i,j])+… f[i]=∑0<j<imin(f[j]+s[i,j])+…,就可以考虑斜率优化(关于斜率: y [ i ] − y [ j ] x [ i ] − x [ j ] \frac{y[i]-y[j]}{x[i]-x[j]} x[i]−x[j]y[i]−y[j])。先设 k 1 k_1 k1< k 2 k2 k2,分析一下是否有 f [ k 1 ] f[k_1] f[k1]< f [ k 2 ] f[k2] f[k2](证明决策单调性),然后大力推一发柿子(通常是把关于 j 1 j1 j1 j 2 j2 j2的项移到坐标,关于i的项移到右边),找到 x x x坐标和 y y y坐标所对应的值,用单调队列维护有用的决策,就可以乱搞ac了
https://loj.ac/problem/10184
这题应该是先教你搞出dp公式
注意题意,执行任务的时间是同一批任务的所用时间总和+启动时间,费用算的是每一个任务所属批次的结束时间*费用系数
设 t [ i ] t[i] t[i]为时间的前缀和, c [ i ] c[i] c[i]为费用的前缀和,$ f [ i ] f[i] f[i]为执行1~i 的最少花费,枚举当前批次的任务为 j ~ i,显然有当前当前批次的费用 ( t [ i ] + s ) ∗ ( c [ i ] − c [ j ] ) (t[i]+s)*(c[i]-c[j]) (t[i]+s)∗(c[i]−c[j]),机器的启动时间S会对后面的所有任务的完成时间都产生一个大小为S的后延,补充到当前的费用中,此时 f [ i ] = ∑ 1 < = j < i m i n ( f [ j ] + ( t [ i ] + S ) ∗ ( c [ i ] − c [ j ] ) + ( c [ n ] − c [ i ] ) ∗ S ) f[i]=\sum_{1<=j<i} min(f[j]+(t[i]+S)*(c[i]-c[j])+(c[n]-c[i])*S) f[i]=∑1<=j<imin(f[j]+(t[i]+S)∗(c[i]−c[j])+(c[n]−c[i])∗S),这题的数据范围 1 ≤ N ≤ 5000 1≤N≤5000 1≤N≤5000,可以放心 N 2 N^2 N2 dp
#include
#include
#include
using namespace std;
int list[5100];
long long f[5100],t[5100],c[5100];
int main()
{
int n,S;
scanf("%d%d",&n,&S);
for (int i=1;i<=n;i++)
{
long long tt,cc;
scanf("%lld%lld",&tt,&cc);
t[i]=t[i-1]+tt;
c[i]=c[i-1]+cc;
}
memset(f,63,sizeof(f));
f[0]=0;
for (int i=1;i<=n;i++)
{
for (int j=0;j
https://loj.ac/problem/10185
(事实证明用t1的 N 2 N^2 N2做法是能过的)
有 j 1 < j 2 j1<j2 j1<j2,试证明 j 2 j2 j2的决策比 j 1 j1 j1优秀
不想证,还是去看进阶指南吧
f [ j 1 ] + ( t [ i ] + S ) ∗ ( c [ i ] − c [ j 1 ] ) + ( c [ n ] − c [ i ] ) ∗ S < f [ j 1 ] + ( t [ i ] + S ) ∗ ( c [ i ] − c [ j 1 ] ) + ( c [ n ] − c [ i ] ) ∗ S f[j1]+(t[i]+S)*(c[i]-c[j1])+(c[n]-c[i])*S<f[j1]+(t[i]+S)*(c[i]-c[j1])+(c[n]-c[i])*S f[j1]+(t[i]+S)∗(c[i]−c[j1])+(c[n]−c[i])∗S<f[j1]+(t[i]+S)∗(c[i]−c[j1])+(c[n]−c[i])∗S
拆括号,抵消,移项,得
f [ j 1 ] − S ∗ c [ j 1 ] − f [ j 2 ] + S ∗ c [ j 2 ] < t [ i ] ∗ c [ j 2 ] − t [ i ] ∗ c [ j 1 ] f[j1]-S*c[j1]-f[j2]+S*c[j2]<t[i]*c[j2]-t[i]*c[j1] f[j1]−S∗c[j1]−f[j2]+S∗c[j2]<t[i]∗c[j2]−t[i]∗c[j1]
右边只保留t[i]的项,
( f [ j 1 ] − S ∗ c [ j 1 ] ) − ( f [ j 2 ] − S ∗ c [ j 2 ] ) − c [ j 1 ] + c [ j 2 ] < t [ i ] \frac{(f[j1]-S*c[j1])-(f[j2]-S*c[j2])}{-c[j1]+c[j2]}<t[i] −c[j1]+c[j2](f[j1]−S∗c[j1])−(f[j2]−S∗c[j2])<t[i]
得到公式啦!
根据“及时排出无用决策”,本题应是要维护一个下凸壳
#include
#include
#include
using namespace std;
int list[11000];
long long f[11000],t[11000],c[11000];
int n,S;
int X(int i)
{
return c[i];
}
int Y(int i)
{
return f[i]-S*c[i];
}
double slop(int j1,int j2)
{
return double(Y(j2)-Y(j1))/double(X(j2)-X(j1));
}
int main()
{
scanf("%d%d",&n,&S);
for (int i=1;i<=n;i++)
{
long long tt,cc;
scanf("%lld%lld",&tt,&cc);
t[i]=t[i-1]+tt;
c[i]=c[i-1]+cc;
}
int head=1,tail=1;
for (int i=1;i<=n;i++)
{
while (head+1<=tail&&slop(list[head],list[head+1])
https://loj.ac/problem/10186
时间可能为负数,因此时间的前缀和t[i]也不具有单调性
而我们要维护斜率还是具有单调性的!!
结束删队头的操作,最后出来的list[head]左边的斜率小于t[i],右边的斜率大于t[i]
所以通过 二分查找到 左边的斜率小于t[i],右边的斜率大于t[i]的点
!!sd模版骗我钱财!! 数字太大还是要把除移项变成乘好一点
#include
#include
#include
using namespace std;
typedef long long LL;
int list[310000];
LL f[310000],t[310000],c[310000];
int n,S;
LL X(int i)
{
return c[i];
}
LL Y(int i)
{
return f[i]-S*c[i];
}
double slop(int j1,int j2)
{
return double(Y(j2)-Y(j1))/double(X(j2)-X(j1));
}
int main()
{
scanf("%d%d",&n,&S);
for (int i=1;i<=n;i++)
{
int tt,cc;
scanf("%d%d",&tt,&cc);
t[i]=t[i-1]+tt;
c[i]=c[i-1]+cc;
}
int head=1,tail=1; list[1]=0;
for (int i=1;i<=n;i++)
{
//while (head+1<=tail&&slop(list[head],list[head+1])=(S+t[i])*(c[list[mid]]-c[list[mid+1]])) l=mid+1; else r=mid;
}
f[i]=f[list[l]]-(S+t[i])*c[list[l]]+t[i]*c[i]+S*c[n];
while (head<=tail-1&&(f[list[tail]]-f[list[tail-1]])*(c[i]-c[list[tail]])>=(f[i]-f[list[tail]])*(c[list[tail]]-c[list[tail-1]])) tail--;
list[++tail]=i;
}
printf("%lld\n",f[n]);
return 0;
}
https://loj.ac/problem/10187
d [ i ] d[i] d[i]为d的前缀和,即1号山到i号山的距离
如果想要接到第i只猫,就得在 t [ i ] − d [ h [ i ] ] t[i]-d[h[i]] t[i]−d[h[i]]时或之后出发,才能在猫玩够之后到达
令 a [ i ] = t [ i ] − d [ h [ i ] ] a[i]=t[i]-d[h[i]] a[i]=t[i]−d[h[i]],对a排序,根据贪心策略,一个饲养员接到的猫一定是a[]排序后连续的一段,那么此时问题又回到了任务安排2了
设 s [ i ] s[i] s[i]为a[ ]的前缀和,f[i][j]为前i个饲养员,接到前j只猫的最小等待时间
有dp方程 f [ i ] [ j ] = m i n ( f [ i − 1 ] [ k ] − ( s [ j ] − s [ k ] ) + a [ j ] ∗ ( j − k ) ) f[i][j]=min(f[i-1][k]-(s[j]-s[k])+a[j]*(j-k)) f[i][j]=min(f[i−1][k]−(s[j]−s[k])+a[j]∗(j−k))
设 k 1 < k 2 k1<k2 k1<k2,因为时间,路程均为正整数,一定有k2优于k1
f [ i − 1 ] [ k 1 ] − ( s [ j ] − s [ k 1 ] ) + a [ j ] ∗ ( j − k 1 ) > f [ i − 1 ] [ k 2 ] − ( s [ j ] − s [ k 2 ] ) + a [ j ] ∗ ( j − k 2 ) f[i-1][k1]-(s[j]-s[k1])+a[j]*(j-k1)>f[i-1][k2]-(s[j]-s[k2])+a[j]*(j-k2) f[i−1][k1]−(s[j]−s[k1])+a[j]∗(j−k1)>f[i−1][k2]−(s[j]−s[k2])+a[j]∗(j−k2)
拆括号,抵消,移项,注意 k 1 − k 2 < 0 k1-k2<0 k1−k2<0,得
( f [ i − 1 ] [ k 1 ] − s [ k 1 ] ) − ( a [ i − 1 ] [ k 2 ] − s [ k 2 ] ) k 1 − k 2 < a [ j ] \frac{(f[i-1][k1]-s[k1])-(a[i-1][k2]-s[k2])}{k1-k2}<a[j] k1−k2(f[i−1][k1]−s[k1])−(a[i−1][k2]−s[k2])<a[j]
上一题血的教训告诉我写代码还是把它弄成乘法吧
#include
#include
#include
using namespace std;
#define LL long long
LL d[110000],a[110000],s[110000],list[110000];
LL f[110][110000];//f[饲养员i][收回前j只猫]
bool cmp(LL x,LL y) {return x=a[j]*(list[head]-list[head+1])) head++;
f[i][j]=min(f[i-1][list[head]]+a[j]*(j-list[head])-(s[j]-s[list[head]]),f[i-1][j]);
if (f[i-1][j]+s[j]>=455743088879883099) continue;
while (head<=tail-1&& (Y(i-1,list[tail-1]) - Y(i-1,list[tail])) * (list[tail]-j) > (Y(i-1,list[tail])-Y(i-1,j))*(list[tail-1]-list[tail])) tail--;
list[++tail]=j;
}
}
printf("%lld\n",f[p][m]);
return 0;
}
https://loj.ac/problem/10188
sum[i]为玩具长度的前缀和
显然有 f [ i ] = ∑ 1 ≤ j < i m i n ( f [ j ] + ( s u m [ i ] − s u m [ j ] + i − j − 1 − L ) 2 ) f[i]=\sum_{1≤j<i}{min(f[j]+(sum[i]-sum[j]+i-j-1-L)^2)} f[i]=∑1≤j<imin(f[j]+(sum[i]−sum[j]+i−j−1−L)2)
令c[i]=sum[i]+i,L=1+L
dp方程: f [ i ] = m i n ( f [ j ] + ( c [ i ] − c [ j ] − L ) 2 ) f[i]=min(f[j]+(c[i]-c[j]-L)^2) f[i]=min(f[j]+(c[i]−c[j]−L)2)
设 k 1 < k 2 k1<k2 k1<k2 (我喜欢用k)
f [ k 1 ] + ( c [ i ] − c [ k 1 ] − L ) 2 < f [ k 2 ] + ( c [ i ] − c [ k 2 ] − L ) 2 f[k1]+(c[i]-c[k1]-L)^2<f[k2]+(c[i]-c[k2]-L)^2 f[k1]+(c[i]−c[k1]−L)2<f[k2]+(c[i]−c[k2]−L)2
把 ( c [ i ] − L ) (c[i]-L) (c[i]−L)看作一个整体,完全平方公式来一发
f [ k 1 ] + ( c [ i ] − L ) 2 − 2 ∗ c [ k 1 ] ∗ ( c [ i ] − L ) + c [ k 1 ] 2 < f [ k 2 ] + ( c [ i ] − L ) 2 + 2 ∗ ( c [ i ] − L ) ∗ c [ k 2 ] + c [ k 2 ] 2 f[k1]+(c[i]-L)^2-2*c[k1]*(c[i]-L)+c[k1]^2<f[k2]+(c[i]-L)^2+2*(c[i]-L)*c[k2]+c[k2]^2 f[k1]+(c[i]−L)2−2∗c[k1]∗(c[i]−L)+c[k1]2<f[k2]+(c[i]−L)2+2∗(c[i]−L)∗c[k2]+c[k2]2
继续拆括号,移项,得
f [ k 1 ] + c [ k 1 ] 2 − ( f [ k 2 ] + c [ k 2 ] 2 ) c [ k 1 ] − c [ k 2 ] < 2 ∗ ( c [ i ] − L ) \frac{f[k1]+c[k1]^2-(f[k2]+c[k2]^2)}{c[k1]-c[k2]}<2*(c[i]-L) c[k1]−c[k2]f[k1]+c[k1]2−(f[k2]+c[k2]2)<2∗(c[i]−L)
#include
#include
#include
using namespace std;
#define LL long long
LL f[51000],list[51000],c[51000];
LL sqr(LL x) {return x*x;}
LL Y(LL i) {return f[i]+sqr(c[i]);}
LL X(LL i) {return c[i];}
int main()
{
int n,L;
scanf("%d%d",&n,&L);
for (int i=1;i<=n;i++)
{
int cc;
scanf("%d",&cc);
c[i]=c[i-1]+cc+1;
}
int head=1,tail=1;
list[0]=0; L++;
for (int i=1;i<=n;i++)
{
while (head+1<=tail && ( Y(list[head]) - Y(list[head+1]) ) >= 2*(c[i]-L) * (X(list[head])-X(list[head+1]))) head++;
f[i]=f[list[head]]+sqr(c[i]-c[list[head]]-L);
while (head<=tail-1 && ( Y(list[tail-1]) - Y(list[tail])) * (X(list[tail])-X(i)) >= ( Y(list[tail]) - Y(i)) * (X(list[tail-1]) - X(list[tail]))) tail--;
list[++tail]=i;
}
printf("%lld\n",f[n]);
return 0;
}
https://loj.ac/problem/10189
注意题意,n是山脚。 其实题目就相当于任务安排2,分割成几段,每段只能选连续的数
当前在i建一个仓库,上一个仓库是在k这个位置建的
有dp方程 f [ i ] = f [ k ] + c o s t ( k + 1 i ) + c [ i ] f[i]=f[k]+cost(k+1~i)+c[i] f[i]=f[k]+cost(k+1 i)+c[i]
需要能够 O ( 1 ) O(1) O(1)查询cost。
先预处理出货物走到工厂1的距离(p[i]*x[i]),新建一个厂就减去剩下的花费
如下图 红色为a的值为⑥走到1的花费,蓝色为(p[k+1]*x[k+1])
假设在⑥建厂,上一个厂建在②,那么③要运到⑥的花费即为两值之差
用a[i]记录 p [ i ] ∗ x [ i ] p[i]*x[i] p[i]∗x[i]的前缀和,pp[i]记录p[i]的前缀和
有dp方程 f [ i ] = m i n ( f [ k ] + ( p p [ i ] − p p [ k ] ) ∗ x [ i ] − ( a [ i ] − a [ k ] ) + c [ i ] ) f[i]=min(f[k]+(pp[i]-pp[k])*x[i]-(a[i]-a[k])+c[i]) f[i]=min(f[k]+(pp[i]−pp[k])∗x[i]−(a[i]−a[k])+c[i])
求斜率方程 ,设k1
拆括号,移项,得
f [ k 1 ] + a [ k 1 ] − ( f [ k 2 ] + a [ k 2 ] ) p p [ k 1 ] − p p [ k 2 ] > x [ i ] \frac{f[k1]+a[k1]-(f[k2]+a[k2])}{pp[k1]-pp[k2]}>x[i] pp[k1]−pp[k2]f[k1]+a[k1]−(f[k2]+a[k2])>x[i]
#include
#include
#include
using namespace std;
#define LL long long
LL f[1100000],x[1100000],pp[1100000],a[1100000];
int list[1100000],c[1100000],p[1100000];
LL X(int i) {return pp[i];}
LL Y(int i) {return f[i]+a[i];}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&x[i],&p[i],&c[i]);
pp[i]=pp[i-1]+p[i];
a[i]=a[i-1]+x[i]*p[i];
}
int head=1,tail=1;
//list[1]=1; memset(f,0,sizeof(f)); f[1]=c[1];
for (int i=1;i<=n;i++)
{
while (head+1<=tail&& Y(list[head]) - Y(list[head+1]) >= x[i] * (X(list[head]) - X(list[head+1])) ) head++;
f[i]=f[list[head]]+(pp[i]-pp[list[head]])*x[i]-(a[i]-a[list[head]])+c[i];
while (head<=tail-1&& (Y(list[tail-1]) - Y(list[tail])) * (X(list[tail]) - X(i)) >= (Y(list[tail]) - Y(i)) * (X(list[tail-1]) - X(list[tail])) ) tail--;
list[++tail]=i;
}
printf("%lld\n",f[n]);
return 0;
}
https://loj.ac/problem/10190
怎么感觉习题比例题还水…
公式也已经给出来了
s[i]为战斗力的前缀和
dp方程: f [ i ] = m i n ( f [ k ] + a ∗ ( s [ i ] − s [ k ] ) 2 + b ∗ ( s [ i ] − s [ k ] + c ) ) f[i]=min(f[k]+a*(s[i]-s[k])^2+b*(s[i]-s[k]+c)) f[i]=min(f[k]+a∗(s[i]−s[k])2+b∗(s[i]−s[k]+c))
斜率方程: f [ k 1 ] − f [ k 2 ] + a ∗ s [ k 1 ] 2 − a ∗ s [ k 2 ] 2 − b ∗ s [ k 1 ] + b ∗ s [ k 2 ] s [ k 1 ] − s [ k 2 ] < 2 ∗ a ∗ s [ i ] \frac{f[k1]-f[k2]+a*s[k1]^2-a*s[k2]^2-b*s[k1]+b*s[k2]}{s[k1]-s[k2]}<2*a*s[i] s[k1]−s[k2]f[k1]−f[k2]+a∗s[k1]2−a∗s[k2]2−b∗s[k1]+b∗s[k2]<2∗a∗s[i]
没开long long见祖宗啊
#include
#include
#include
using namespace std;
#define LL long long
LL f[1100000],s[1100000];
int list[1100000],n,a,b,c;
LL sqr(LL x) {return x*x;}
LL Y(int i) {return f[i]+a*sqr(s[i])-b*s[i];}
LL X(int i) {return s[i];}
int main()
{
scanf("%d%d%d%d",&n,&a,&b,&c);
for (int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
s[i]=s[i-1]+x;
}
int head=1,tail=1; //f[1]=a*sqr(s[1])+b*s[1]+c; list[2]=1;
for (int i=1;i<=n;i++)
{
while (head+1<=tail&& (Y(list[head]) - Y(list[head+1])) <(2*a*s[i])*(X(list[head]) -X(list[head+1]))) head++;
f[i]=f[list[head]]+a*sqr(s[i]-s[list[head]])+b*(s[i]-s[list[head]])+c;
while (head<=tail-1&& (Y(list[tail-1]) - Y(list[tail])) * (X(list[tail]) - X(i)) < (Y(list[tail]) - Y(i)) * (X(list[tail-1]) - X(list[tail]))) tail--;
list[++tail]=i;
}
printf("%lld\n",f[n]);
return 0;
}
https://loj.ac/problem/10191
也是给出了公式了
c[i]为花费的前缀和
dp方程: f [ i ] = m i n ( f [ k ] + ( s [ i ] − s [ k ] ) 2 + M ) f[i]=min(f[k]+(s[i]-s[k])^2+M) f[i]=min(f[k]+(s[i]−s[k])2+M)
斜率方程: f [ k 1 ] − f [ k 2 ] + s [ k 1 ] 2 − s [ k 2 ] 2 s [ k 1 ] − s [ k 2 ] > 2 ∗ s [ i ] \frac{f[k1]-f[k2]+s[k1]^2-s[k2]^2}{s[k1]-s[k2]}>2*s[i] s[k1]−s[k2]f[k1]−f[k2]+s[k1]2−s[k2]2>2∗s[i]
多组数据
#include
#include
#include
using namespace std;
#define LL long long
LL f[510000],s[510000];
int list[510000];
LL sqr(LL x) {return x*x;}
LL Y(int i) {return f[i]+sqr(s[i]);}
LL X(int i) {return s[i];}
int main()
{
int n,m;
while (scanf("%d%d",&n,&m)!=EOF)
{
s[0]=0;
for (int i=1;i<=n;i++)
{
scanf("%lld",&s[i]);
s[i]+=s[i-1];
}
int head=1,tail=1; list[1]=0; f[0]=0;
for (int i=1;i<=n;i++)
{
while (head+1<=tail&& (Y(list[head]) - Y(list[head+1])) >= 2*s[i]*(X(list[head])-X(list[head+1]))) head++;
f[i]=f[list[head]]+sqr(s[i]-s[list[head]])+m;
while (head<=tail-1&& (Y(list[tail-1]) - Y(list[tail])) * (X(list[tail])-X(i)) >= (Y(list[tail]) - Y(i)) * (X(list[tail-1]) - X(list[tail]))) tail--;
list[++tail]=i;
}
printf("%lld\n",f[n]);
}
return 0;
}
https://loj.ac/problem/10192
a[i]和前面仓库建设的定义是一样的
设第一个仓库建在k,第二个仓库建在i ,且k f [ i ] = c o s t ( 1 , k ) + c o s t ( k + 1 , i ) + c o s t ( i + 1 , n + 1 ) f[i]=cost(1,k)+cost(k+1,i)+cost(i+1,n+1) f[i]=cost(1,k)+cost(k+1,i)+cost(i+1,n+1)
dp方程: f [ i ] = m i n ( f [ i ] + s [ k ] ∗ d [ k ] − a [ k ] + ( s [ i ] − s [ k ] ) ∗ d [ i ] − ( a [ i ] − a [ k ] ) + ( s [ n + 1 ] − s [ i ] ) ∗ d [ n ] − ( a [ n ] − a [ i ] ) ) f[i]=min(f[i]+s[k]*d[k]-a[k]+(s[i]-s[k])*d[i]-(a[i]-a[k])+(s[n+1]-s[i])*d[n]-(a[n]-a[i])) f[i]=min(f[i]+s[k]∗d[k]−a[k]+(s[i]−s[k])∗d[i]−(a[i]−a[k])+(s[n+1]−s[i])∗d[n]−(a[n]−a[i]))
斜率方程:(注意s[k1] f [ i ] = s [ k 1 ] ∗ d [ k 1 ] − s [ k 2 ] ∗ d [ k 2 ] s [ k 1 ] − s [ k 2 ] < d [ i ] f[i]=\frac{s[k1]*d[k1]-s[k2]*d[k2]}{s[k1]-s[k2]}<d[i] f[i]=s[k1]−s[k2]s[k1]∗d[k1]−s[k2]∗d[k2]<d[i]
#include
#include
#include
using namespace std;
#define LL long long
LL f[210000],w[210000],d[210000],a[210000];
int list[210000];
LL Y(LL i) {return w[i]*d[i];}
LL X(LL i) {return w[i];}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
d[i+1]=d[i]+y;
a[i+1]=a[i]+x*d[i];
w[i]=w[i-1]+x;
}
int head=1,tail=1; //list[1]=1; f[1]=a[n]-a[1];
LL ans=9223372036854775807;
for (int i=1;i<=n;i++)
{
while (head+1<=tail&& (Y(list[head]) - Y(list[head+1])) >= d[i] * (X(list[head]) - X(list[head+1]))) head++;
f[i]=w[list[head]]*d[list[head]]-a[list[head]]+(w[i]-w[list[head]])*d[i]-(a[i]-a[list[head]])+(w[n]-w[i])*d[n+1]-(a[n+1]-a[i]);
while (head<=tail-1&& (Y(list[tail-1]) - Y(list[tail])) * (X(list[tail]) - X(i)) >= (Y(list[tail]) - Y(i)) * (X(list[tail-1]) - X(list[tail]))) tail--;
list[++tail]=i;
ans=min(ans,f[i]);
}
printf("%lld\n",ans);
return 0;
}
完结撒花!!
肝死我了这篇blog