JZOJ-senior-3620. 【BOI2011】trapezoid

3620. 【BOI2011】trapezoid

(File IO): input:trapezoid.in output:trapezoid.out
Time Limits: 500 ms Memory Limits: 65536 KB Detailed Limits Special Judge
Goto ProblemSet

Description

考虑任意两条选中的横线。一个介于两线之间的梯形Ti 有两个顶点位于上面的直线,另外两个顶点位于下面的直线。我们用ai, bi, ci 及di 分别代表梯形左上、右上、左下及右下的顶点。所有梯形的集合的一个子集S 被成为独立的,仅当任意两个S 中的梯形不相交。

确定最大的梯形独立子集的基数(最大集合意味着拥有最多元素的集合)。并且找到不同的最大独立集的数量。找到的方案数模30013。

Input

输入的第一行含一个整数N——梯形的数目。接下来N 行每行包含四个整数ai;,bi, ci 及di。没有两个梯形的顶点(角)重合。

Output

输出的唯一一行应包含两个用空格隔开的数字:第一个,最大独立子集的基数;第二个,不同最大独立子集数目模30013 后的值。

Sample Input

7

1 3 1 9

4 7 2 8

11 15 4 12

10 12 15 19

16 23 16 22

20 22 13 25

30 31 30 31

Sample Output

3 8

Data Constraint

• 1 <= N <= 100 000

• 1 <= ai, bi, ci, di <= 1 000 000 000

• 如果只有输出的第一个数正确,那么你可以得到测试点40% 的分数。

• 40% 的数据有N <= 5000

Hint

Solution

暴力DP的方程很好想,就是一个最长不下降子序列的模型。
f[i]表示前i个梯形构成最大的梯形独立子集的基数。

f[i] = max{f[j]+1;|(满足梯形i与j不相交时)}
不同的最大独立集的数量在转移是一同维护。

这样做是N^2的,所以我们要优化。

首先我们把梯形端点坐标分别离散化(上面的一起离散化,下面的一起离散化),后面说的端点的值都是离散化之后的。

我们按照梯形两个下端点的顺序做。

1)假如当前做到的是一个右下端点,假设这个右下端点属于梯形j。
则我们在线段树的j梯形右上端点位置上插入f[j]值。

2)假如当前做到的是一个左下端点,假设这个左下端点属于梯形j。

f[i] = Find(梯形j的左上端点-1) + 1; Find(x)是查询插入的位置在1~x中的f值最大值。

首先我们要清楚,线段树中的元素都保证了右下端点在当前左下端点的前面,即梯形下面不相交。

其次我们树状数组是按照梯形的右上端点插入的,所以我们Find(梯形j的左上端点-1)就保证了取到的是梯形的上面不相交时的最大f值。

不同的最大独立集的数量在线段树中同时维护。

Code

#include<algorithm>
#include<cstdio>
#define N 100010
#define P 30013
using namespace std;
int n,m,s1,s2,a1,a2;
int num[N],sum[N],t[8*N],t1[8*N];
struct node {int a,b,c,d;}e[N];
struct node1 {int x,y,z;}f[2*N],g[2*N];
bool cmp(node1 p,node1 q) {return p.x<q.x;}
void find(int x,int st,int en,int l,int r)
{
    if (st==l && en==r) 
    {
        if (t[x]>a1) a1=t[x],a2=t1[x];
        else if (t[x]==a1) a2=(a2+t1[x])%P;
        return;
    }
    int mid=(st+en)>>1;
    if (r<=mid) find(x+x,st,mid,l,r);
    else if (l>mid) find(x+x+1,mid+1,en,l,r);
    else find(x+x,st,mid,l,mid),find(x+x+1,mid+1,en,mid+1,r);
}
void change(int x,int st,int en,int p,int v1,int v2)
{
    if (st==en) {t[x]=v1; t1[x]=v2; return;}
    int mid=(st+en)>>1;
    if (p<=mid) change(x+x,st,mid,p,v1,v2);
    else change(x+x+1,mid+1,en,p,v1,v2);
    if (t[x+x]>t[x+x+1]) t[x]=t[x+x],t1[x]=t1[x+x];
    else if (t[x+x]<t[x+x+1]) t[x]=t[x+x+1],t1[x]=t1[x+x+1];
    else t[x]=t[x+x],t1[x]=(t1[x+x]+t1[x+x+1])%P;
}
int main()
{
    freopen("trapezoid.in","r",stdin);
    freopen("trapezoid.out","w",stdout);
    scanf("%d",&n),m=2*n;
    for(int i=1; i<=n; i++) 
    {
        scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].c,&e[i].d);
        f[2*i-1].x=e[i].a,f[2*i-1].y=i,f[2*i-1].z=1;
        f[2*i].x=e[i].b,f[2*i].y=i,f[2*i].z=2;
        g[2*i-1].x=e[i].c,g[2*i-1].y=i,g[2*i-1].z=3;
        g[2*i].x=e[i].d,g[2*i].y=i,g[2*i].z=4;
    }
    sort(f+1,f+1+m,cmp),sort(g+1,g+1+m,cmp);
    for(int i=1; i<=m; i++)
    {
        if (f[i].z==1) e[f[i].y].a=i+1; else e[f[i].y].b=i+1;
        if (g[i].z==3) e[g[i].y].c=i+1; else e[g[i].y].d=i+1;
        f[i].x=g[i].x=i+1;
    }
    for(int i=1; i<=m; i++)
    {
        int w=g[i].y;
        if (g[i].z==3)
        {
            a1=a2=0,find(1,1,m,1,e[w].a-1),num[w]=a1+1,sum[w]=a2;
            if (sum[w]==0) sum[w]++;
            if (num[w]>s1) s1=num[w],s2=sum[w];
            else if (num[w]==s1) s2=(s2+sum[w])%P;
        }
        else change(1,1,m,e[w].b,num[w],sum[w]);
    }
    printf("%d %d",s1,s2);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值