不谈离散数学基本定理
本文半娱乐向半学术向
先列出定理:
-
1.对于 \(\forall x,y \in \mathbb{Z},x<y\),有 \(x+1\le y\)
-
2.\(\forall a,b\in\mathbb{Z},a<b,x>1\),则有 \(x^a<x^b\)
-
3.\(\forall i\in\{1,2\cdots,n\},a_i\in\mathbb{N_+}\),且 \(\sum_{i=1}^n a_i=S\),则必定存在 \(k_1,k_2\in\{1,2\cdots n\},a_{k_1}\le\frac{S}{n},a_{k_2}\ge\frac{S}{n}\)
-
4.\(\forall x,y\in\mathbb{R},x=x+y\times 0,x=x+y-y\)
-
1.对于 \(\forall x,y \in \mathbb{Z},x<y\),有 \(x+1\le y\)
推论:对于 \(\forall x_1,x_2 \cdots x_n \in \mathbb{Z},x_1<x_2< \cdots <x_n\),有 \(x_1+n-1 \le x_2+n-2 \le \cdots \le x_n\)
典例分析:


Solution
首先观察到 \(d\in[0,1]\) 想到分情况讨论,对于 \(d=0\) 显然是单调队列优化DP的板子题,对于 \(d=1\) 我们发现对于一个 \(f_i\) 他最多就会加一,根据“离散数学基本定理”可以知道,一个不为最大值的 \(f_i\) 加一之后也无法影响最大值,所以可以忽略不为最大值的 \(f_i\) 所以需要做的就是写一个线段树找 \(f_i\) 最小的中的 \(h_i\) 最大的一个即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,d,h[300010],w[300010],f[300010];
struct node
{
int l,r,pre,mmax;
}t[1200010];
struct node1
{
node1(){}
node1(int pre,int mmax):pre(pre),mmax(mmax) {}
int pre,mmax;
};
void build(int x,int a,int b)
{
t[x].l=a;t[x].r=b;
if(a==b)
{
t[x].pre=0;t[x].mmax=h[a];
return ;
}
int mid=a+b>>1;
build(x*2,a,mid);
build(x*2+1,mid+1,b);
t[x].pre=min(t[x*2].pre,t[x*2+1].pre);
if(t[x*2].pre==t[x*2+1].pre)t[x].mmax=max(t[x*2].mmax,t[x*2+1].mmax);
else if(t[x*2].pre>t[x*2+1].pre)t[x].mmax=t[x*2+1].mmax;
else t[x].mmax=t[x*2].mmax;
return ;
}
void change(int x,int a,int b,int s)
{
if(a<=t[x].l&&b>=t[x].r)
{
t[x].pre=s;
return ;
}
int mid=t[x].l+t[x].r>>1;
if(a<=mid)
{
change(x*2,a,b,s);
}
if(b>mid)
{
change(x*2+1,a,b,s);
}
t[x].pre=min(t[x*2].pre,t[x*2+1].pre);
if(t[x*2].pre==t[x*2+1].pre)t[x].mmax=max(t[x*2].mmax,t[x*2+1].mmax);
else if(t[x*2].pre>t[x*2+1].pre)t[x].mmax=t[x*2+1].mmax;
else t[x].mmax=t[x*2].mmax;
}
node1 query(int x,int a,int b)
{
if(a<=t[x].l&&b>=t[x].r)
{
return node1(t[x].pre,t[x].mmax);
}
int mid=t[x].l+t[x].r>>1;
node1 ans1,ans2;
if(a<=mid)
{
ans1=query(x*2,a,b);
}
if(b>mid)
{
ans2=query(x*2+1,a,b);
}
if(a<=mid&&b>mid)
{
int pre=min(ans1.pre,ans2.pre),mmax;
if(ans1.pre==ans2.pre)mmax=max(ans1.mmax,ans2.mmax);
else if(ans1.pre>ans2.pre)mmax=ans2.mmax;
else mmax=ans1.mmax;
return node1(pre,mmax);
}
else if(a<=mid)return ans1;
else if(b>mid)return ans2;
else return node1(0x3f,0x3f);
}
signed main()
{
scanf("%lld%lld%lld",&n,&k,&d);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&h[i],&w[i]);
}
build(1,1,n);
change(1,1,1,w[1]);
for(int i=2;i<=n;i++)
{
node1 ans=query(1,max(i-k,1ll),i-1);
f[i]=ans.pre+w[i]+(ans.mmax<=h[i])*d;
change(1,i,i,f[i]);
}
cout<<f[n]<<endl;
return 0;
}
赛时写的抽象代码。
- 2.对于 \(\forall x\),有 \(x=x+y\times0\)。
看起来抽象实际非常具体,看例题:
\(A(z)=\sum a_nz^z\),\(B(z)=\sum a_nz^n\),\(C(z)=\sum c_nz^n\)
试用 \(A(z)\) 和 \(B(z)\) 表示 \(C(z)\)。
我们首先发现这个东西是一个卷积的形式但不完全是卷积的形式。
但是我们可以使用高贵的换元法:
到这里,不知道这个重要的离散数学基本定理的人一定就不会做了,但是,我们会!发现只有 \(j\) 为偶数时才会计数,但是我们完全可以让奇数项的值为零即可,所以变成了没脑子题。
所以他就是一个卷积再求和的形式,所以答案为:
要睡觉了以后再写。

浙公网安备 33010602011771号