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;
}

浙公网安备 33010602011771号