洛谷P3336 [ZJOI2013] 话旧

72 阅读6分钟

首先题目要求最大值,那么默认是有解的。

将这些点(要把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;
}