【描述】
二维平面上有n个点(xi, yi),现在这些点中取若干点构成一个集合S,对它们按照x坐标排序,顺次连接,将会构成一些连续上升、下降的折线,设其数量为f(S)。如下图中,1->2,2->3,3->5,5->6(数字为下图中从左到右的点编号),将折线分为了4部分,每部分连续上升、下降。
现给定k,求满足f(S) = k的S集合个数。(
)
【思路】
这题其实挺简单的。首先对点按x排序,从左往右考虑转移。考虑转移时我们需要什么:1.点的位置 2.当前形成的折线方向 3.当前构成的折线段数。所以定义f[i][j][0/1]表示以第i个点结尾,已经构成了j段折线,折线方向向上/向下。转移很显然:
考虑前缀和优化,但是y[k]<y[i]的限制很讨厌。注意到点的坐标很小,我们可以用树状数组来优化转移。
代码:
#include<bits/stdc++.h>
#define re register
#define mp make_pair
using namespace std;
const int N=1e5+5,mod=1e5+7;
inline int red(){
int data=0;bool w=0; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return w?-data:data;
}int n,m,mx;
struct point{
int x,y;
friend inline bool operator<(const point&a,const point&b){return a.x<b.x;}
}p[N];
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a<b?a-b+mod:a-b;}
inline void Add(int&a,const int&b){((a+=b)>=mod)&&(a-=mod);}
struct tree{
int c[N];
inline void add(int x,const int&v){while(x<=mx)Add(c[x],v),x+=x&(-x);}
inline int query(int x){int ret=0;
while(x)Add(ret,c[x]),x-=x&(-x);return ret;
}
}bit[11][2];
int main(){n=red();m=red();
for(int re i=1;i<=n;i++)p[i]=(point){red(),red()},mx=max(mx,p[i].y);
sort(p+1,p+n+1);
for(int re i=1;i<=n;i++){
bit[0][0].add(p[i].y,1);
bit[0][1].add(p[i].y,1);
for(int re k=1;k<=m;k++){
bit[k][0].add(p[i].y,add(bit[k][0].query(p[i].y-1),bit[k-1][1].query(p[i].y-1)));
bit[k][1].add(p[i].y,add(dec(bit[k][1].query(mx),bit[k][1].query(p[i].y)),dec(bit[k-1][0].query(mx),bit[k-1][0].query(p[i].y))));
}
}cout<<add(bit[m][0].query(mx),bit[m][1].query(mx))<<"\n";
}