地址:https://vijos.org/p/1002
题意:一条直线上有m个点,青蛙一次能跳的长度为s~t,每个点的坐标范围1~10^9,1<=s<=t<=10
分析:这题一下子就能想到简单的DP,f[ i ] =min{ f[ j +k ] }+w (s<=k<=t, w为i坐标是否有石子),这样的复杂度是O(L*t)的,主要时间都在L上,由于石头数m比较少,我们应该会发现,实际上有许多石头相隔很远,这之间的状态一直都是相同的,所以没必要全部考虑,而是直接跳过,我们可以将这座桥分段,每段为石子加上它两端的t个坐标,如果两个石子太近,就把他们和起来成为一段,这样最多只有m段,现在需要更新的状态只有相邻两段之间的状态转移,还有本段自身的转移,本段自身的转移方程很简单,就是上面的方程,而两段之间的转移复杂一点,就是前一段的后t个坐标和,当前段的前t个坐标,注意这里只能取t个才能保证之间没有石子,这也是状态不改变的前提,现在就是枚举前面t个到当前t个是否可达了,可达就更新,在这里我郁闷了好久,数论功底太弱= =,现在总结一下:
坐标i到j是否可达,也就是x*s+y*t = j-i+1,这个式子不就是典型的二元一次不定方程ax+by=c
一般的不定方程我们只求出可行解就行了,而这里我们需要的是非负解,因为题目要求青蛙一直向终点跳。。。
所先,省略一堆证明,我们可以用拓展欧几里德求出一组特解x0,y0,或则判断无解
现在重点就是根据这组特解求出非负解,我们知道x=x0-bt, y=y0+at, t=0.+-1, +-2,+-3...也是方程的解,现在要求x=x0-bt>=0, y=y0+ay>=0
得到(-y0/a)<=t<=(x0/b)现在我们只要判断这个区间是否有一个整数就行了,有整数说明有解
现在我们可以判断任意长度是否可以跳到了,所以整个解法完成,剩下的就是代码了
有个细节就是石子的坐标无序
PS:这题好久以前怎么写的忘了,记得好简单的啊。。。不过当时好像是暴力拓展105左右的长度来保证正确性,证明我也不会,总之数学弱渣了。。。
代码越写越挫:
#include
#include
#include
#include
using namespace std;
const int mm=111111;
const int mn=111;
struct data
{
int s,t,p;
}g[111];
int f[mm],a[mn],b[mm];
int i,j,s,t,m,n,l,ans;
int getp(int u, int x)
{
return g[u].p+x-g[u].s;
}
int extended_euclid(int a, int b, int &x, int &y)
{
if(b == 0) // gcd(a, b) == a
{
x = 1;
y = 0;
return a;
}
int n = extended_euclid(b, a%b, x, y);
int tmp = x;
x = y;
y = tmp - (a/b)*y;
return n;
}
int ok(int d)
{
int x,y,n;
n=extended_euclid(s,t,x,y);
if(d%n)return 0;
x=x*(d/n);
y=y*(d/n);
for(n=-y/s-1;n<=x/t+1;++n)
if(-y<=s*n&&n*t<=x)return 1;
return 0;
}
void dp(int u, int v)
{
int i,j,k;
for(i=g[v].s;i<=g[v].t;++i)
for(b[getp(v,i)]=0,j=1;j<=m-2;++j)
if(a[j]==i)++b[getp(v,i)];
if(v>1||g[v].s>0)
for(i=max(g[u].t-t,g[u].s);i<=g[u].t;++i)
for(j=g[v].s;j<=min(g[v].s+t,g[v].t);++j)
if(ok(j-i))f[getp(v,j)]=min(f[getp(v,j)],f[getp(u,i)]+b[getp(v,j)]);
for(i=g[v].s+s;i<=g[v].t;++i)
{
for(j=s;j<=t;++j)
if(i-j>=g[v].s)f[getp(v,i)]=min(f[getp(v,i)],f[getp(v,i-j)]+b[getp(v,i)]);
}
}
int main()
{
scanf("%d",&l);
scanf("%d%d%d",&s,&t,&m);
for(i=1;i<=m;++i)
scanf("%d",&a[i]);
sort(a+1,a+m+1);
a[++m]=l;
a[++m]=2e9;
n=0;
g[0].s=g[0].t=g[0].p=0;
for(j=1,i=2;i<=m;++i)
if(a[i]>a[i-1]+2*t)
{
++n;
g[n].s=max(0,a[j]-t);
g[n].t=a[i-1]+t;
g[n].p=g[n-1].p+g[n-1].t-g[n-1].s+1;
j=i;
}
memset(f,10,sizeof(f));
f[0]=0;
if(g[1].s==0)f[1]=0;
for(i=1;i<=n;++i)dp(i-1,i);
ans=m+1;
for(i=g[n].s;i<=g[n].t;++i)
if(i>=l&&f[g[n].p+i-g[n].s]