题解 P3336 [ZJOI2013]话旧

分析

首先是对极小值均是0的理解,它的意思即为在每一次函数斜率改为-1后,一定要一直下降到0才可以。

因此我们就可以给方案数另一种理解方式,在1到 n 之间,可以选择多少种不同的函数下降点,使它经过所有的确定点。

这样一来就好分析多了,我们可以将问题转化为每两个定点之间,前一个的方案数可以如何转移到后一个的方案数,而到每一个定点时会有两种情况,上升或是下降,因此对于动态规划数组,我们在第二维用0代表到该定点正在上升的方案数,1代表下降。

容易想到最简单的情况就是横坐标之差恰好为纵坐标之差,这样二者之间的转移方式只能为一条斜率恒定的线段。

但如果纵坐标之差大于横坐标之差呢?前面说了,可以将方案数转为可以换为选择多少种不同的下降点,因此我们就考虑如果将左端点按斜率-1向右下降至0,右端点按斜率为1向左下降至0,它们之间是否还有多余的操作空间来加以修改。

因此我们定义一个整数 h,使 \(h=x2-x1-y1-y2\),如果 \(x<0\),那么说明无法先下降再上升回去,因此直接将前一个上升方案赋给它的下降方案。如果等于0,就说明可以刚好下降后上升到该点,下降赋值不变,上升则是前一点的两种方案之和,因为上升期间可以随意下降。

对于 \(h>0\) 的情况呢?相当于中间的这一段我们可以不断地选择不同的分割方式,分成数段反复上升和下降的片段,而最小的一段长度应为2,所以在一开始计算 h 时我们就应将它除以2,这样的话就相当于将 h 任意分割了,方案数则是 \(2^{h-1}\),而对于前一点上升的方案来说,它还可以继续上升,选择其它位置斜率转为下降,因此在数组转移时前一个上升的方案要乘2再赋值。

那对于函数最大值呢?我们想到贪心,在前一个可以上升,后一个转移后发现可以下降进入该点的情况下,我们就可以前一个点持续上升,上升到刚好一直下降就可以到后一点时,就是这一段可以到达的函数最大值,至此本题就解决了。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int mod=19940417;
int n,k,mx;
void add(int &xx,int yy){xx=(xx+yy)%mod;}
inline void read(int &res){
	res=0;
	char c=getchar();
	while(c<‘0‘||c>‘9‘)c=getchar();
	while(c>=‘0‘&&c<=‘9‘)res=(res<<1)+(res<<3)+c-48,c=getchar();
}
inline int qpow(int ds,int zs){
	if(zs==0)return 1;
	int x=1;
	while(zs){
		if(zs&1)x=x*ds%mod;
		ds=ds*ds%mod;zs>>=1;
	}
	return x;
}
struct node{
	int x,y;
}a[1000010];
bool operator<(node aa,node bb){return aa.x<bb.x;}
bool operator==(node aa,node bb){return aa.x==bb.x&&aa.y==bb.y;}
int f[1000010][2];
signed main(){
	read(n);
	read(k);
	for(int i=1;i<=k;i++)read(a[i].x),read(a[i].y);
	a[++k]=(node){0,0};a[++k]=(node){n,0};
	sort(&a[1],&a[k+1]);k=unique(&a[1],&a[k+1])-a-1;
	f[1][1]=1;
	for(int i=1;i<k;i++){
		int h=a[i+1].x-a[i].x-a[i+1].y-a[i].y;h>>=1;
		if(a[i+1].y-a[i].y==a[i].x-a[i+1].x)f[i+1][1]=(f[i][1]+f[i][0])%mod;
		else if(a[i+1].y-a[i].y==a[i+1].x-a[i].x)f[i+1][0]=(f[i][0]+(a[i].y?0:f[i][1]))%mod;
		else if(h<0)f[i+1][1]=f[i][0];
		else if(h==0)f[i+1][0]=(f[i][1]+f[i][0])%mod,f[i+1][1]=f[i][0];
		else {
			int j=qpow(2,h-1);
			if(a[i+1].y)f[i+1][0]=(f[i][1]+2LL*f[i][0])*j%mod;
			f[i+1][1]=(f[i][1]+2LL*f[i][0])*j%mod;
		}
		if((f[i+1][1]||a[i+1].y==0)&&(f[i][0]||a[i].y==0))mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x+a[i].y)/2);
	}
	cout<<f[k][1]%mod<<" "<<mx<<endl;
	return 0;
}

题解 P3336 [ZJOI2013]话旧

上一篇:设计模式-模板方法模式


下一篇:可见格式化模型