Codeforces Round #722 (Div. 2) D. Kavi on Pairing Duty (数论递推)
-
题意:将\(2n\)个点两两相连形成\(n\)对,对于任意两个点对\(A\)和\(B\),要求至少满足其中一条:1.\(A\)和\(B\)的某一个完全包含于另一个中 2.\(A\)和\(B\)的长度相等.问你一共有多少种方案.
-
题解:假设第一个区间的左端点为\(1\),右端点为\(x\),对\(x\)分两种情况来分析.
1.\(x> n\),那么所有大于\(x\)的点,因为它不完全包含于\([1,x]\),所以它们的长度都相同,左端点确定,那么在\([1,x]\)内,就会有一些点空出来,这些点我们可以看成是一种子情况,所以可以用前缀和来维护.注意,如果没有点空出来,也算是一种情况.
2.\(x\le n\),那么所有的点对长度相同,我们连接\(1\)和\(x\)后,\([1,x]\)中间会空出\(x-2\)个点,将中间的这些点和对应的右端点连接,全部连完后,最右端点扩展到了\(x+x-2\),即\(2*(x-1)\),那么我们可以将这看成一个完整的块,这个块有\(2*(x-1)\)个点,很明显,只有\(2n \mod 2*(x-1)=0\)时才合法, 所以\(x-1\)一定要是\(n\)的因子(不含\(1\)).
那么我们就能得出递推式子:\(dp[i]=pre[i-1]+divisors[i]\).
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 998244353; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} ll n; ll a[N]; ll divs[N]; void pre(){ //n*logn for(int i=1;i<=1000000;++i){ for(int j=i;j<=1000000;j+=i){ divs[j]++; } } } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); pre(); ll cur=4; a[1]=1; a[2]=3; cin>>n; for(int i=3;i<=n;++i){ a[i]=(cur+divs[i])%mod; cur=(cur+a[i])%mod; } cout<<a[n]<<'\n'; return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮