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

written on 2022-07-12

n 的范围极小,因此可以想到先预处理出所有互质的数对,然后再做考量。

然后手模一下一些数据,很容易可以将这些数对转化为一条条线段,那么根据题意,答案即为:从这些线段中任选若干条使得它们能覆盖整个区间的方案数。

经计算机测试,线段数最多有 127 条,因此搜索肯定是不可行的,那么对于这类区间线段覆盖方案数的问题,事实上解决此类问题的典型套路是使用动态规划。

  1. 状态设计:动态规划设计状态的第一步是确定阶段,这一题的阶段很显然就是目前已选择的线段数量。但是仅有这一维是无法转移的,那么事实上对于区间线段覆盖的题目,将值域作为第二维往往是有效的处理方案。因此对于这一题,我们设 fi,j 表示目前已经处理了前 i 条线段,值域为 [1,j] 的部分已经被完全覆盖的方案数。

  2. 转移方程:考虑一个阶段中,一条线段有选与不选两种决策。若不选,那么直接转移,即 fi,j=fi1,j;若选,那么原先在这条线段上的所有值域均可以转移,即 fi,Ri=fi,Ri+fi,j,j[Li,Ri]

观察式子,为了保证没有后效性,我们应当先将所有线段按右端点递增的顺序排序。

当然转移方程还存在问题。对于选取这条线段的情况,还存在这条线段对转移不存在贡献的那部分!因此,转移中容易被忽略的一个式子是:fi,j=fi,j+fi1,j,j[1,Li)

考试的时候想到要用动态规划,但是被状态的第二维设计给卡住了,说明我还是太菜了,基础太烂,题目做得太少。另外还有一点总结一下,就是转移时最好明确三种方式:

  1. 不选的情况。

  2. 选了有贡献的情况。

  3. 选了也没有贡献的情况。

要不然很容易导致转移错误。

#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]);
}
posted @   Freshair_qprt  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示