[APIO2016]划艇

题目描述

在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着 NNN 个划艇学校,编号依次为 111 到 NNN。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为 iii 的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在 aia_ia?i?? 至 bib_ib?i?? 之间任意选择(ai≤bi)。

值得注意的是,编号为 iii 的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。

输入所有学校的 ai,bia_i, b_ia?i??,b?i?? 的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同。

输入格式

第一行包括一个整数 NNN,表示学校的数量。
接下来 NNN 行,每行包括两个正整数,用来描述一所学校。其中第 iii 行包括的两个正整数分别表示 ai,bi(1≤ai≤bi≤109)a_i, b_i($1\le a_i\le b_i\le 10^9$)a?i??,b?i??(1≤a?i??≤b?i??≤10?9??)。

输出格式

输出一行,一个整数,表示所有可能的派出划艇的方案数除以 109+710^9+710?9??+7 得到的余数。

数据范围与提示

子任务 1(9 分):1≤N≤5001 \le N \le 5001≤N≤500 且对于所有的 1≤i≤N1 \le i \le N1≤i≤N,保证 ai=bia_i=b_ia?i??=b?i??。

子任务 2(22 分):1≤N≤5001 \le N \le 5001≤N≤500 且 ∑i=1N(bi−ai)≤106\sum_{i=1}^{N}(b_i-a_i) \le 10^6∑?i=1?N??(b?i??−a?i??)≤10?6??。

子任务 3(27 分):1≤N≤1001 \le N \le 1001≤N≤100。

子任务 4(42 分):1≤N≤5001 \le N \le 5001≤N≤500。

题解 

  31分算法

  设f[i][j]表示第i个学校派j(a[i]<=j<+b[i])个划艇,且i以后的学校都不派划艇(为了保证不重不漏,想一下就知道了),那么f[i][j]就等于$\sum \limits_{l=1}^{i-1}\sum\limits_{r=1}^{j-1}f[l][r]+1$

  这就相当于在以l为最后一个派划艇的学校后在加一个学校i,原来f[l][r]仍属于总答案的一部分,+1是只有i选择派出的情况

  枚举i,j,l,r,复杂度O(n^4),观察上面的柿子,可以用树状数组(有人用的是线段树)维护前缀和,那么省去l,r

两层循环,只要在当前树状数组中找到1~j-1 前缀和,就是f[i][j],再加到树状数组中就可以了,因此f[][]可以省略。

  注意要倒着枚举j,以免正着枚举f[i][j-1]加进数组后影响f[i][j],当然也可以用数组先记录一下,等当前层i的j枚举一遍之后在插入

  不要忘了离散化a[i]~b[i];

#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 7;
map<long long, int> p1, p2;
int n, num, a[510], b[510], sum[210000000], p[12200000], s[1000000];
int lowbit(int x) { return x & (-x); }
void add(int x, int d) {
    while (x <= num) {
        sum[x] = (sum[x] + d) % mod;
        x += lowbit(x);
    }
}
int ask(int x) {
    int ans = 0;
    while (x) {
        ans = (ans + sum[x]) % mod;
        x -= lowbit(x);
    }
    return ans % mod;
}
int main() {
    //    freopen("boat.in","r",stdin);
    //    freopen("boat.out","w",stdout);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &a[i], &b[i]);
        for (int j = a[i]; j <= b[i]; j++) {
            if (p1[j] == 0)
                p1[j] = 1, s[++s[0]] = j;
        }
    }
    sort(s + 1, s + s[0] + 1);
    for (int i = 1; i <= s[0]; i++) p2[s[i]] = ++num;
    long long ans = 0;
    for (int i = 1; i <= n; i++) {
        a[i] = p2[a[i]], b[i] = p2[b[i]];
        for (int j = b[i]; j >=a[i] ; j--) {
            int w= ask(j - 1) % mod;
            ans = (ans + w+ 1) % mod;
            add(j, w + 1);
        }
    }
    printf("%lld\n", ans % mod);
}

31分代码

  100分算法

  上面的思路,即使离散化也会超时,因为它枚举的j是a[i]~b[i],那么考虑一下可不可以只枚举a[i],b[i].

显然是可以的,将a[i],b[i]+1存进s[],离散化后为了不重不漏设j-1~j区间长度s[j]-s[j-1],左闭右开(建议不太清楚的朋友们手模一下)

  设f[i][j]表示最后一个派出划艇的学校是i,派出了j-1~j个中的一个数的总方案数

  方案分两部分:

  1. i从j-1~j选一个,前面所有的学校要么不选要么从1~j-1区间中选,方案数 $\sum\limits_{l=1}^{i-1}\sum\limits_{r=1}^{j-1} f[l][r]*C_len^1$

  2.i从j-1~j选一个,前面有学校也从j-1~j中选,定第一个从j-1~j中选的学校为k,显然总方案数为(k~i%&^%*的一堆方案数)* $\sum\limits_{l=1}^{k-1}\sum\limits_{r=1}^{j-1} f[l][r]$

  首先1.和2.中 $\sum\limits_{l=1}^{i-1}\sum\limits_{r=1}^{j-1} f[l][r]$  $\sum\limits_{l=1}^{k-1}\sum\limits_{r=1}^{j-1} f[l][r]$ 可以用二维前缀和表示

  然后就是求2.柿子前面那一堆了

  可知k~i学校一定选择j-1~j中的数或者选择0,因为k和i都从j区间中选,k+1~i-1学校可以选择j区间的如果选的话一定从j区间中选,如果不选就选0,而不可以选j区间的一定不能派划艇

  先另起一个话题

    从长度为len(元素各不相同)的区间中选择n个数,使满足数列严格递增

    答案很明显 $C_len^n$

    有3个长度为3(元素各不相同)的相同区间,每个区间可以选一个数也可以不选,使选出来的数严格递增

    选0个区间 方案数$C_3^0*C_3^0$

    选1个区间            $C_3^1*C_3^1$

    选2个区间            $C_3^2*C_3^2$

    选3个区间            $C_3^3*C_3^3$

    方案总数 $C_3^1*C_3^1+C_3^1*C_3^1+C_3^2*C_3^2+C_3^3*C_3^3$

    由$C_n^m=C_n^{n-m}可知,以上式子可以换成$C_3^3*C_3^0+C_3^2*C_3^1+C_3^1*C_3^2+C_3^0*C_3^3$

    这是一个从3+3个人里选3个人的问题,前三人中选3人那后三人中只能选0人$\cdots$以次类推,方案数为$C_6^3$

  言归正转,设[k,i]区间中可以选j区间的个数为p,   j区间长度为len,则问题转化成有p个相同的长度为len的区间,可以选可以不选,使选出的数严格递增

另外,为了避免不重不漏,k和i必选,这个后面再做解释,式子为:

        $C_{p-2}^0*C_{len}^2+C_{p-2}^1*C_{len}^{3}+C_{p-2}^2*C_{len}^4+\cdots+C_{p-2}^{p-2}*C_{len}^p$

  同样可以看出方案数 $C_{l+p-2}^{p},定义来求一定会TLE,由于l过大也不能用杨辉三角,所以用到了$C_{n+1}^{m+1}=C_n^m*\frac{n+1}{m+1}$ 递推

  说到不重不漏,i必选是肯定的(f[i][j]就是这么定义的%¥%¥……),k如果不一定选,当k+1作为第一个选j区间的学校,此时k一定不选,这就与k作为第一个而k不选,k+1有可能选有重复

  p表示[k,i]之间可选j区间的学校个数,倒序枚举k,同时记录p(遇到满足条件的就p++),同时由p递推组合数,注意p一开始为1,k==i-1是p==2……

  二维前缀和f[i][j],表示1~i学校从1~j区间选的方案总数

       $f[i][j]=len*f[i-1][j-1]+\sum\limits_{k=i-1}^{1} C_{l+p-2}^p*f[k-1][j-1] $

  f[n][m]即为最终答案(二维前缀和嘛)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <map>
 5 #include <vector>
 6 using namespace std;
 7 const int mod = 1e9 + 7;
 8 map<long long, int> p1, p2;
 9 int n, num;
10 long long a[510], b[510],s[1010], f[510][1010],inv[510],l[1010];
11 int main() {
12     scanf("%d", &n);
13     inv[1]=1;
14     for(int i=2;i<=500;i++){
15         inv[i]=(long long)((mod-mod/i)*inv[mod%i])%mod;
16     }
17     for (int i = 1; i <= n; i++) {
18         scanf("%lld%lld", &a[i], &b[i]);
19         if (p1.count(a[i])==0)
20             p1[a[i]] = 1, s[++s[0]] = a[i];
21         if (p1.count(b[i]+1)==0)
22             p1[b[i]+1] = 1, s[++s[0]] = b[i]+1;
23     }
24     sort(s + 1, s + s[0] + 1);
25     for (int i = 1; i <= s[0]; i++){
26         p2[s[i]] = ++num;
27         l[num]=s[i]-s[i-1];
28     }
29     for (int i = 1; i <= n; i++) a[i] = p2[a[i]]+1, b[i] = p2[b[i]+1];
30     for(int i=0;i<=num;i++) f[0][i]=1;
31     for (int i = 1; i <= n; i++) {
32         f[i][0]=1;
33         for (int j = a[i]; j <=b[i]; j++) {
34             f[i][j]=(long long)f[i-1][j-1]*l[j]%mod;
35             int now=1,c=l[j]-1;
36             for (int k=i-1; k; k--) {
37                 if(a[k]<=j&&b[k]>=j){
38                     now++;
39                     c=((((long long)c*(l[j]+now-2))%mod)*inv[now])%mod;
40                     if(!c) break;
41                     f[i][j]=((long long)f[i][j]+(long long)c*f[k - 1][j - 1])%mod;
42                 }
43             }
44         }
45         for(int j=1;j<=num;j++){
46             f[i][j]=(((long long)f[i][j]+f[i][j-1]+f[i-1][j]-f[i-1][j-1])%mod+mod)%mod;
47         }
48     }
49     printf("%lld", (f[n][num]-1+mod)%mod);
50 }

原文地址:https://www.cnblogs.com/heoitys/p/11166727.html

时间: 2024-10-13 10:56:34

[APIO2016]划艇的相关文章

计数难题5:[APIO2016]划艇

计数难题5:[APIO2016]划艇 标签(空格分隔): 计数难题题选 题目大意: 给定\(n\)个区间\([l_i,r_i]\). 你可以从第\(i\)个区间中选出一个整数元素\(a_i\in [l_i,r_i]\),也可以不选. 要求选出的元素按标号顺序排列后构成一个严格单调递增序列. 求至少选出一个元素的合法方案数. 答案对\(10^9+7\)取模. 数据范围:\(n\leq 500\) , \(l_i\leq r_i \leq 10^9\) . 题解 可以想到把区间离散化. 设\(f_{

[Codeforces 1295F]Good Contest(DP+组合数学)

[Codeforces 1295F]Good Contest(DP+组合数学) 题面 有一个长度为\(n\)的整数序列,第\(i\)个数的值在\([l_i,r_i]\)中随机产生.问这个序列是一个不上升序列的概率(模\(998244353\)意义下). \(n \leq 50,l_i,r_i \leq 998244351\) 分析 和[APIO2016]划艇几乎一模一样.可惜比赛的时候时间不够. 首先把问题转化成求最长不上升序列的数量. 我们把这些区间离散化,分割成两两之间不互相覆盖的若干个区间

【loj2567】【APIO2016】划艇

题目 \(N\)个位置,每个位置要么不选,要么选\([ a_i, b_i ]\)中的一个数: 问最后的单调上升序列(mod 1e9+7)有多少种: \(1 \le N \le 500\) 题解 orz abclzr 直接\(dp\)最后一位是什么数字的话只能得到31分 将数字离散化分段,第\(i\)段为\([l_i,r_i)\),设\(f_{i,j}\)表示第i个位置选的数字在第j段的方案数(第0段表示没有) \[ f_{i,j} \ = \sum_{k=0}^{i-1} \sum_{l=0}^

BZOJ 4584 【APIO2016】 赛艇

题目链接:赛艇 讲道理好好的Boat为啥要翻译成赛艇呢--题面中不也是划艇么-- 这道题考虑一下dp.由于划艇数量过于庞大,所以肯定不能直接记录到dp状态中.所以一个想法就是把数量离散化,然后把每个学校的数量在哪一段内记录下来.也就是说\(f_{i,j,k}\)表示前\(i\)个学校,第\(i\)所学校派出的划艇数量在区间\(j\)内,并且区间\(j\)内共有\(k\)个学校的方案数.然后分类讨论一下转移: 当\(k\ne 1\)时,有: \begin{aligned}f_{i,j,k} &=\

[BZOJ4584][Apio2016]赛艇

试题描述 在首尔城中,汉江横贯东西.在汉江的北岸,从西向东星星点点地分布着个划艇学校,编号依次为到.每个学校都拥有若干艘划艇.同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同.颜色相同的划艇被认为是一样的.每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加.如果编号为的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在Ai至Bi之间任意选择(Ai<=Bi).值得注意的是,编号为i的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校

CTSC2016&amp;&amp;APIO2016爆零记

CTSC2016&&APIO2016爆零记 前言:人生第一次写(骗)爆(访)零(问)记(量),心里还是有点小激动呢.不过由于本文作者语文水平低下,如果发现用词不当或只会记流水账,请谅解. CTSC Day0: CCF大发慈悲让我们入住了5星级酒店昆(百)泰酒店. 不过也有一点不兹磁的地方,就是酒店离学校大概要步行半个小时,对于我这种懒人大概是天大的灾难吧(雾).于是这次比赛就强行变成了CTSC&&APIO远足营. 第一次看见70块钱一瓶零下八度不结冰的矿泉水,然而家穷喝不起

UOJ#204 【APIO2016】Boat

Time Limit: 70 Sec  Memory Limit: 256 MBSubmit: 559  Solved: 248 Description 在首尔城中,汉江横贯东西.在汉江的北岸,从西向东星星点点地分布着个划艇学校,编号依次为到.每个学校都 拥有若干艘划艇.同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同.颜色相同的划艇被认为是一 样的.每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加.如果编号为的学校选择 派出划艇参加庆典,那么,派出的划艇数量可

【APIO2016】Gap

[APIO2016]Gap 交互题 对于30%的数据,直接从s=0,t=1e18开始,每次让s=mn+1,t=mx-1,每一次可以得到两个,可以在(n+1)/2的时间内得到 对于其他数据,我们先求出最大最小值,求出len=(mx-mn)/(n-1),这相当与是一个"平均数",如果我将区间分为若干段长度为len的区间,只记录每个区间内的最大值和最小值,这样被漏算的那些点,他们最多有len-1的贡献,而这个还达不到平均数,所以肯定不会影响答案,所以就可以在3*n以内求出序列有用的部分了.

BZOJ 4584 [Apio2016]赛艇 ——动态规划

Subtask 1 直接$N^2$ $DP$,就可以了 Subtask 2 用$f[i][j]$表示当前位置为$i$,结束元素为$j$的方案数. Subtask 3 看下面 Subtask 4 首先可以枚举一段序列选择同一个重叠的区间,然后一些可以不选,如果选的话要求上升. 然后很容易得到方程,离散化之后就可以$N^4$直接做了. 然后并不会优化,看了题解. 想了想写不出来. 抄代码啦! #include <map> #include <ctime> #include <cm