BZOJ3688: 折线统计

Portal

3688: 折线统计

Time Limit: 10 Sec   Memory Limit: 256 MB

Description

二维平面上有n个点(xi, yi),现在这些点中取若干点构成一个集合S,对它们按照x坐标排序,顺次连接,将会构成一些连续上升、下降的折线,设其数量为f(S)。如下图中,1->2,2->3,3->5,5->6(数字为下图中从左到右的点编号),将折线分为了4部分,每部分连续上升、下降。

 

现给定k,求满足f(S) = k的S集合个数。


Input

第一行两个整数n和k,以下n行每行两个数(xi, yi)表示第i个点的坐标。所有点的坐标值都在[1, 100000]内,且不存在两个点,x坐标值相等或y坐标值相等

Output

输出满足要求的方案总数 mod 100007的结果

Sample Input


5 1

5 5

3 2

4 4

2 3

1 1

Sample Output


19



HINT


对于100%的数据,n <= 50000,0 < k <= 10

先按x坐标排序
f[i][j][0/1] 表示前 i 个构成j个折线,最后一个折线为上升/下降的方案数。
f[i][j][0]=i1k=1f[k][j][0]+f[k][j1][1]   (y[k]<y[i])
f[i][j][1]=i1k=1f[k][j][1]+f[k][j1][0]   (y[k]>y[i])

用个树状数组维护一下就好了

【代码】

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 50005
#define mod 100007
#define INF 1000000001
using namespace std;
typedef long long ll;

int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,tot,ans;
int Hash[N],szsz[11][2][N];
int f[N][11][2];

class Point{
    public:
        int x,y;
}e[N];

bool operator <(Point a,Point b){
    return a.x<b.x;
}

int lowbit(int x){
    return x&-x;
}

void Update(int x,int y,int j,int k)
{
    while(x<=tot)
    {
        szsz[j][k][x]=(szsz[j][k][x]+y)%mod;
        x+=lowbit(x);
    }
}

int Query(int x,int j,int k)
{
    int rtn=0;
    while(x)
    {
        rtn=(rtn+szsz[j][k][x])%mod;
        x-=lowbit(x);
    }
    return rtn;
}

void Input_Init()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) e[i].x=read(),e[i].y=Hash[i]=read();
    sort(e+1,e+1+n);
    sort(Hash+1,Hash+1+n);
    tot=unique(Hash+1,Hash+1+n)-Hash-1;
}

int Find(int x)
{
    int l=1,r=tot,rtn;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(Hash[mid]>=x) rtn=mid,r=mid-1;
        else l=mid+1;
    }
    return rtn;
}

void DP()
{
    for(int i=1;i<=n;i++)
    {
        static int tx;
        tx=Find(e[i].y);f[i][0][0]=f[i][0][1]=1;
        Update(tx,1,0,0);Update(tx,1,0,1);
        for(int j=1;j<=m;j++)
        {
            f[i][j][0]+=Query(tx-1,j,0)+Query(tx-1,j-1,1);
            f[i][j][1]+=Query(tot,j,1)-Query(tx,j,1)+Query(tot,j-1,0)-Query(tx,j-1,0);
            f[i][j][0]%=mod;f[i][j][1]%=mod;
            if(f[i][j][1]<0) f[i][j][1]+=mod;
            Update(tx,f[i][j][0],j,0);Update(tx,f[i][j][1],j,1);
        }
    }
    for(int i=1;i<=n;i++)
    {
        ans+=f[i][m][0];if(ans>=mod) ans-=mod;
        ans+=f[i][m][1];if(ans>=mod) ans-=mod;
    }
    printf("%d\n",ans);
}

int main()
{
    Input_Init();
    DP();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值