BZOJ 3688
3688: 折线统计
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 119 Solved: 66
[Submit][Status][Discuss]
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
5 5
3 2
4 4
2 3
1 1
Sample Output
19
HINT
对于100%的数据,n <= 50000,0 < k <= 10
Source
FJ2014
思路就是三维动归,但是如果每一位都用for枚举的话要O(kn^2),太慢了
所以用树状数组维护一下a[i].y以下的所有f数组的前缀和,起到了降维的作用
注意树状数组每一次都应该要维护到N
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #define N 100000+5 #define K 10+2 #define mod 100007 #define lowbit(x) x&(-x) using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s>'9' || s<'0'){if(s=='-')f=-1;s=getchar();} while(s<='9' && s>='0'){x=x*10+s-'0';s=getchar();} return x*f; } struct node{int x,y;}a[N]; bool operator < (node a,node b) {return a.x<b.x;} int f[12][N][2],n,k; void add(int k,int x,int opt,int val) { for(int i=x;i<=N;i+=lowbit(i)) f[k][i][opt]=(f[k][i][opt]+val)%mod; } int ask(int k,int x,int opt) { int ans=0; for(int i=x;i;i-=lowbit(i)) ans=(ans+f[k][i][opt])%mod; return ans%mod; } int main() { n=read();k=read(); for(int i=1;i<=n;i++)a[i].x=read(),a[i].y=read(); sort(a+1,a+n+1); for(int i=1;i<=n;i++) { add(0,a[i].y,0,1),add(0,a[i].y,1,1); for(int j=1;j<=k;j++) { add(j,a[i].y,0, ((ask(j,N,0)-ask(j,a[i].y,0)+ask(j-1,N,1)-ask(j-1,a[i].y,1))%mod+mod)%mod ); add(j,a[i].y,1, (ask(j-1,a[i].y-1,0)+ask(j,a[i].y-1,1))%mod ); } } printf("%d\n",(ask(k,N,0)+ask(k,N,1))%mod); return 0; }