首先题目要求最大值,那么默认是有解的。
将这些点(要把0,0和n,n包含进来)按照x从小到大排序,去重,此时记得到k个点。
由于极小值必须是0,点i和点(i-1)之间的连线是什么样和点i的左斜率是+1还是-1有关系,我们把一个点的左斜率是+1叫做这个点升,-1叫做这个点降,定义状态:
f[i][0]:第i个点升,从第1个点(0,0)到第i个点升的线段共有多少个
f[i][1]:第第i个点降,从第1个点(0,0)到第i个点升的线段共有多少个
从点j=(i-1)到点i如何转移状态?点j是左边的点,点i是右边的点,dx=x2-x1,dy=y2-y1,如果dx=dy或者dx=-dy,那么只有一种连线方式(直上或者直下),原因是y的增量的绝对值最大就是x的增量的绝对值,必须每次x增加1,y都选择向上1或者每次都选择向下1,才能达到这个状况。
其他情况按照i和j之间的连线有没有“落地”来考虑,怎么样一定不会落地?
如果len=x2-x1-y1-y2,则len有如下情况:
因为求len已经最尽量让两边的点落地了,如果len<0,则两点只能空中连接(两个点都在空中),此时只有左上(左边点的左斜率是+1)右下一种可能。
如果len>=0,可以分为左上右上,左上右下,左下右上,左下右下四种情况,这里挑两类谈谈。 左下右上是最简单的情况:
这里小坡数量=len/2,可以移动的凹槽数量p=小坡数量-1,则这里(两点之间)有2^p种线段,因为每个凹槽可以选择移动或者不移动。
左上右下:
此时p=len/2,有2^p种线段在A和B间。你可能会注意到A虽然是上但是图中没有让他继续往上走,而是直接下来,B是下,只向左升了一个就下来了,这样做是先构造最宽松的条件,然后通过小坡、凹槽的情况获得所有移动线段改造图形的可能,这些是构成所有图形的“元”。
有一个细节需要注意,len必须是偶数,否则线段无法相交在格点上。但由于本题一定有解,所以不用判断len是不是偶数。
求最大值的时候,每次都让两个相邻点之间的线段尽量先向上走再向下走,这样可以吗,其实不行,因为中途某段线段先向上再向下的方案不一定被最终采用。比如数据:
14 4
2 2
3 1
11 1
12 2
只要分别记录上当前点的状态其实就可以,ma1[i]表示从第一个点(0,0)到点i,且第i个点是上升点的情况下的最大值,ma2[i]是下降点。根据转移情况的不同,比如len>0时的“左升右降”,前一个点是j,后一个点是i,那么ma2[i]由i和j之间的线段以及ma1[j]更新。最后的答案是ma2[k]。
在实现的时候注意一些细节:
len=0的情况单独考虑更好(我的代码里没有单独考虑,“左下右上”的情况下,len=0,p=len/2-1<0,2^p被定义成0,但其实这里存在着一种连线情况)。
在比如“左下右上”这样转移的时候,右点的y2>0才行。
在进行最大值ma1,ma2的更新的时候,要保证前一个点的连线方式存在才可以,比如左上右下,左j右i,f[j][0]>0,才能用i和j之间的线段最高点来更新ma2[i]。不然都没有这种连线的可能,哪里来的最大值。
最大值还有可能在输入给出的所有点的y值去到,可以一开始遍历所有y值,这样在更新ma1和ma2的时候就不考虑两点端点了。
#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
#define fio ios::sync_with_stdio(0);cin.tie(0);
#define fin freopen("D:/in.txt","r",stdin);
#define fout freopen("D:/out.txt","w",stdout);
const ll maxn=1e6+10,mod=19940417;
struct Point{
ll x,y;
bool operator < (const Point &rhs) const {
return x<rhs.x;
}
bool operator == (const Point &rhs) const {
return x==rhs.x && y==rhs.y;
}
}p[maxn];
ll n,k;
ll f[maxn][2],ma1[maxn],ma2[maxn];
ll binpow(ll a,ll n){
if(n<0) return 0;
ll res=1;
a%=mod;
while(n) {
if(n&1) res=res*a%mod;
a=a*a%mod;
n>>=1;
}
return res;
}
ll up_down(ll x1,ll y1,ll x2,ll y2){
ll res=y1+(x2-x1+y2-y1)/2;
return res;
}
int main()
{
fio;
//fin;
cin>>n>>k;
for(ll i=1;i<=k;i++){
cin>>p[i].x>>p[i].y;
}
p[k+1].x=0;p[k+1].y=0;
p[k+2].x=n;p[k+2].y=0;
k+=2; //共有k个点
stable_sort(p+1,p+1+k);
k=unique(p+1,p+1+k)-(p+1);
//cout<<k<<"\n";
//看是否存在一个x对应多个y
ll flag=1;
for(ll i=2;i<=k;i++){
if(p[i].x==p[i-1].x) {flag=0;break;}
}
if(flag==0) cout<<0<<" "<<0<<"\n";
else {
f[1][0]=0;f[1][1]=1; //初始化
ll maxy=0;
for(ll i=1;i<=k;i++) maxy=max(maxy,p[i].y);
for(ll i=2;i<=k;i++){
ll j=i-1;
ll x2=p[i].x,y2=p[i].y;
ll x1=p[j].x,y1=p[j].y;
if(y2-y1==x2-x1) {
f[i][0]=(f[i][0]+f[j][0])%mod;
if(f[j][0]!=0) ma1[i]=max(ma1[i],ma1[j]);
if(y1==0) {
f[i][0]=(f[i][0]+f[j][1])%mod;
if(f[j][1]!=0) ma1[i]=max(ma1[i],ma2[j]);
}
}else if(y2-y1==x1-x2){
f[i][1]=(f[i][1]+f[j][0]+f[j][1])%mod;
ma2[i]=max(ma2[i],max(ma1[j],ma2[j]));
}else {
ll len=(x2-x1)-(y1+y2);
if(len%2!=0) continue;
if(len<0){ //先上后下
f[i][1]=(f[i][1]+f[j][0])%mod;
if(f[j][0]!=0) ma2[i]=max(ma2[i],max(ma1[j],up_down(x1,y1,x2,y2)));
}
else{
//左上右上
if(y2>0) {
f[i][0]=(f[i][0]+binpow(2,len/2)*f[j][0]%mod)%mod;
if(f[j][0]!=0) ma1[i]=max(max(ma1[i],up_down(x1,y1,x2-y2,0)),ma1[j]);
}
//左上右下
f[i][1]=(f[i][1]+binpow(2,len/2)*f[j][0]%mod)%mod;
if(f[j][0]!=0) ma2[i]=max(max(ma2[i],up_down(x1,y1,x2,y2)),ma1[j]);
//左下右上
if(y2>0) {
f[i][0]=(f[i][0]+binpow(2,len/2-1)*f[j][1]%mod)%mod;
if(len==0) f[i][0]=(f[i][0]+f[j][1]%mod)%mod;
if(f[j][1]!=0) ma1[i]=max(max(up_down(x1+y1,0,x2-y2,0),ma2[j]),ma1[i]);
}
//左下右下
f[i][1]=(f[i][1]+binpow(2,len/2-1)*f[j][1]%mod)%mod;
if(f[j][1]!=0) ma2[i]=max(ma2[i],max(up_down(x1+y1,0,x2,y2),ma2[j]));
}
}
}
maxy=max(maxy,ma2[k]);
cout<<(f[k][1]+mod)%mod<<" ";
/*for(ll i=1;i<=k;i++){
printf("f[%lld][0]=%lld f[%lld][1]=%lld\n",i,f[i][0],i,f[i][1]);
}*/
cout<<maxy<<"\n";
}
return 0;
}