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

洛谷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$ 个下降,则有

则最大值为

但是要注意,如果可以选择向上,一定有 $dp[i][0]\ne0$

因此对于所有的 $i$ 到 $i+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 !
评论
  目录