P7800 [COCI2015-2016#6] PAROVI 题解

读完题后可以首先发现,编号 \(1\)\(n\) 的点都是必须要选的,否则 \(\text{Slavko}\) 可以选择 \(2\) 或者 \(n\) 取得胜利。

而对于中间的数如果存在两对数 \((l_1,r_1)\) \((l_2,r_2)\)\(l_1< l_2\)\(r_1\le l_2\) 那么可以选择数 \(l_2\) 取胜。

那么只有 \(r_1>l_2\) 时,取 \(l_2\) 是无法取胜的,此时若把两对数看成一个区间,那么就可以将本题看成一个左闭右开的区间覆盖问题。

由于求方案数,考虑 dp。

预处理出来所有互质的数对,并按左区间为关键字排序。

\(f_{i,j}\) 代表对于第 \(i\) 个区间,已经覆盖到第 \(j\) 格的方案数。

初始化:对于数对 \((l_i,r_i)\),如果 \(l_i=1\),那么 \(f_{i,r_i}=1\)

转移:

选择这个区间:\(f_{i,\max(j,r_i)}=f_{i,\max(j,r_i)}+f_{i-1,j}(l_i\le j)\)

不选这个区间:\(f_{i,j}=f_{i,j}+f_{i-1,j}\)

答案即为 \(f_{num,n}\hspace{0.1cm}\text{其中 num 代表互质数对个数}\)

复杂度约为 \(O(n^3)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int mod=1e9;
const int N=25;
int n,cnt,f[N*N][N];
int gcd(int x,int y)
{
	if(y==0)return x;
	return gcd(y,x%y);
}
struct node
{
	int l,r;
}a[N*N];
int cmp(node fi,node se)
{
	return fi.r>se.r;
}
signed main()
{
	freopen("parovi.in","r",stdin);
	freopen("parovi.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)if(gcd(i,j)==1)a[++cnt]=(node){i,j};
	}
	for(int i=1;i<=cnt;i++)
	{
		if(a[i].l==1)f[i][a[i].r]=1;
		for(int j=1;j<=n;j++)
		{
			f[i][j]+=f[i-1][j];
			f[i][j]%=mod;
			if(a[i].l<=j)f[i][max(j,a[i].r)]+=f[i-1][j],f[i][max(j,a[i].r)]%=mod;
		}
	}
	int ans=f[cnt][n];
	printf("%lld",ans);
	return 0;
}
posted @ 2023-02-24 13:29  Gmt丶Fu9ture  阅读(21)  评论(0)    收藏  举报