嘘~ 正在从服务器偷取页面 . . .

洛谷P3336 [ZJOI2013]话旧 题解


洛谷P3336 [ZJOI2013]话旧 题解

题目链接:P3336 [ZJOI2013]话旧

题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 paper,一跃成为教授,也成为了银河队选拔委员会成员。

一日,小林与金教授聊天。金教授回忆起过去的岁月,那些年他学过的电路原理。他曾经对一种三角波很感兴趣,并且进行了一些探究。小林感到很好奇,于是金教授就将课题形式化地说了一遍。

有一定义在 \([0,N]\) 的连续函数 \(f(x)\),其中 \(N\) 是整数,满足 \(f(0)=f(N)=0\),它的所有极值点在整数处取到,且 \(f(x)\)极小值均是 \(0\)。对于任意的 \(0\)\(N-1\) 间的整数 \(I\)\(f(x)\)\((I, I+1)\) 上是斜率为 \(1\)\(-1\) 的一次函数。

金先生研究的是,若他知道其中 \(K\) 个整点的函数值,那么:

  1. 有多少个函数满足条件?
  2. 满足条件的函数中,\(f(x)\) 的最大值,最大能是多少?

小林思考了一下,便想出了很好的算法。那么作为经过多年训练的你呢?

输入格式

第一行包含两个用空格隔开的整数 \(N,K\)。接下来 \(K\) 行,每行两个整数,表示 \(x_i\)\(f(x_i)\)

输出格式

一行两个整数,分别对应两个问题的答案。考虑到第一问答案可能很大,你只要输出它除以 \(19940417\) 的余数。

输入输出样例

输入 #1

2 0

输出 #1

1 1

输入 #2

6 9
4 2
4 2
2 0
4 2
6 0
5 1
2 0
0 0
0 0

输出 #2

1 2

说明/提示

  • 对于 \(10\%\) 的数据,\(N \leq 10\)
  • 对于 \(20\%\) 的数据,\(N \leq 50\)
  • 对于 \(30\%\) 的数据,\(N \leq 100\)\(K \leq 100\)
  • 对于 \(50\%\) 的数据,\(N \leq 10^3\)\(K \leq 10^3\)
  • 对于 \(70\%\) 的数据,\(N \leq 10^5\)
  • 另有 \(10\%\) 的数据,\(K=0\)
  • 对于 \(100\%\) 的数据,\(0 \leq N \leq 10^9\)\(0 \leq K \leq 10^6\)

题目说了极小值为 \(0\) ,说明每次下降都是直接一口气到底的

从文字中可以看出题目保证了有解(不然怎么输出最大值)

考虑设计状态 \(dp[i][0/1]\) 表示是需要访问到 \(i\) 结点时的路径是向上还是向下的

  • \(dp[i][1] \to dp[i+1][0]\)

    显然延长左右的两条路径,可得零点 \(a,b\)

    然后在 \(a,b\) 间反复横跳便可以相连了

    \(a,b\) 间的距离为 \(2k\) ,则方案数为 \(2^{k-1}\)

    如下图 \((2k=6)\)

    \          /
     \        /
      \/\/\/\/

    观察到其形成了 \(4\)\/ 的结构,不妨其为齿孔

    \          /
     \  /\    /
      \/  \/\/

    注意到此时除了两侧的齿孔不可扩展以外,其余均可扩展

    故方案数为 \(2^2 = 4\)

  • \(dp[i][0] \to dp[i+1][0]\)

    不妨将 \(i\) 与其向上的路径看作一个点,则又可以确定两个零点 \(a,b\)

    而此时左侧的齿孔也可以扩展了,而右侧的仍不可,故方案数为 \(2^k\)

    如下图 \((2k=4)\)

    /\        /
      \      /
       \/\/\/

    扩展左侧齿孔会变成

     /\      /
    /  \    /
        \/\/

    故方案数为 \(2^2 = 4\)

  • \(dp[i][1]\to dp[i+1][1]\)

    与上一种情况类似,将 \(i+1\) 与其向下的路径看作一个点

    右侧的齿孔可以扩展,而此时左侧的不可,故方案数为 \(2^k\)

  • \(dp[i][0] \to dp[i+1][1]\)

    考虑将 \(i+1\) 与其向下的路径看作一个点

    则此时左侧的齿孔可以扩展,而右侧的不可以

    故方案数为 \(2^k\)

然而有些情况会导致 \(k<0\) ,因此要特判一下

还有一些特殊的情况,比如 \(i+1\) 恰好在 \(i\) 的向上/下的路径上,也需要特判

然后最大值的求解,根据贪心,一定先选掉所有的上升

设有 \(a\) 个上升, \(b\) 个下降,则有 \[ \begin{cases} a+b = x_2-x_1 \\ a-b = y_2-y_1 \end{cases} \] 则最大值为 \[ y_1+a = \dfrac{1}{2}(x_2+y_2-x_1 + y_1) \] 但是要注意,如果可以选择向上,一定有 \(dp[i][0]\ne0\)

因此对于所有的 \(i\)\(i+1\) 的转移,都额外计算一下先走到底再上升的情况,即 \[ \dfrac{1}{2}(x_2+y_2-x_1-y_1) \] 代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N (int)(1e6+15)
const int p=19940417;
int n,k,mx;
void add(int &a,int b){a=((a+b%p)%p+p)%p;}
int qpow(int a,int b)
{
    int ans=1,base=a%p;
    while(b)
    {
        if(b&1)ans=ans*base%p;
        base=base*base%p;
        b>>=1;
    }
    return ans;
}
struct node
{
    int x,y;
}a[N];
bool operator<(node a,node b)
    {return a.x<b.x;}
bool operator==(node a,node b)
    {return a.x==b.x&&a.y==b.y;}
int dp[N][2];
signed main()
{
    read(n);read(k);
    for(int i=1; i<=k; i++)
        read(a[i].x),read(a[i].y);
    a[++k]={0,0};a[++k]={n,0};
    sort(a+1,a+1+k);k=unique(a+1,a+1+k)-a-1;
    dp[1][1]=1;
    for(int i=1; i<k; i++)
    {
        int d=(a[i+1].x-a[i].x-a[i+1].y-a[i].y)>>1;
        mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x-a[i].y)>>1);
        if(dp[i][0])mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x+a[i].y)>>1);
        if(a[i+1].y-a[i].y==a[i].x-a[i+1].x)
            add(dp[i+1][1],dp[i][0]+dp[i][1]);
        else if(a[i+1].y-a[i].y==a[i+1].x-a[i].x)
            add(dp[i+1][0],dp[i][0]+(a[i].y?0:dp[i][1]));
        else if(d<0)
            add(dp[i+1][1],dp[i][0]);
        else if(d==0)
            add(dp[i+1][0],dp[i][0]+dp[i][1]),add(dp[i+1][1],dp[i][0]);
        else
        {
            int c=qpow(2,d-1);
            if(a[i+1].y)add(dp[i+1][0],(dp[i][1]+2ll*dp[i][0]%p)%p*c);
            add(dp[i+1][1],(dp[i][1]+2ll*dp[i][0]%p)%p*c);
        }
    }
    printf("%lld %lld\n",dp[k][1],mx);
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录