[COCI2015-2016#6] PAROVI(区间线段覆盖方案数)

· · 题解

然后手模一下一些数据,很容易可以将这些数对转化为一条条线段,那么根据题意,答案即为:从这些线段中任选若干条使得它们能覆盖整个区间的方案数。 经计算机测试,线段数最多有 $127$ 条,因此搜索肯定是不可行的,那么对于这类区间线段覆盖方案数的问题,事实上解决此类问题的典型套路是使用动态规划。 1. 状态设计:动态规划设计状态的第一步是**确定阶段**,这一题的阶段很显然就是目前已选择的线段数量。但是仅有这一维是无法转移的,那么事实上对于区间线段覆盖的题目,将**值域**作为第二维往往是有效的处理方案。因此对于这一题,我们设 $f_{i,j}$ 表示目前已经处理了前 $i$ 条线段,值域为 $[1,j]$ 的部分已经被完全覆盖的方案数。 2. 转移方程:考虑一个阶段中,一条线段有选与不选两种决策。若不选,那么直接转移,即 $f_{i,j}=f_{i-1,j}$;若选,那么原先在这条线段上的所有值域均可以转移,即 $f_{i,R_i}=f_{i,R_i}+f_{i,j},j\in [L_i,R_i]$。 观察式子,为了保证没有后效性,我们应当先将所有线段按右端点递增的顺序排序。 当然转移方程还存在问题。对于选取这条线段的情况,还存在这条线段对转移不存在贡献的那部分!因此,转移中容易被忽略的一个式子是:$f_{i,j}=f_{i,j}+f_{i-1,j},j\in [1,L_i)$。 考试的时候想到要用动态规划,但是被状态的第二维设计给卡住了,~~说明我还是太菜了,基础太烂,题目做得太少~~。另外还有一点总结一下,就是转移时最好明确三种方式: 1. 不选的情况。 2. 选了有贡献的情况。 3. 选了也没有贡献的情况。 要不然很容易导致转移错误。 ```cpp #include<bits/stdc++.h> #define N 130 using namespace std; const int mod=1e9; int n,tot; struct F{int x,y;}a[N]; int gcd(int a,int b){return b?gcd(b,a%b):a;} bool cmp(F a,F b){return a.y<b.y;} int f[N][25]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(gcd(i,j)==1) a[++tot]=(F){i,j}; sort(a+1,a+1+tot,cmp); f[0][1]=1; for(int i=1;i<=tot;i++) { //不选这条线段 for(int j=1;j<=n;j++) f[i][j]=f[i-1][j]; //选了这条线段 //有贡献的部分 for(int j=a[i].x;j<=a[i].y;j++) f[i][a[i].y]=(f[i][a[i].y]+f[i-1][j])%mod; //不贡献的部分 for(int j=1;j<a[i].x;j++) f[i][j]=(f[i][j]+f[i-1][j])%mod; } printf("%d\n",f[tot][n]); } ```