返回顶部

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;
    }
    
    
posted @ 2021-05-25 15:37  Rayotaku  阅读(248)  评论(0编辑  收藏  举报