• atcoder arc106 D Powers


    原题链接:Arc106 D Powers

    题目大意:给定正整数(n,k)以及一数列({a})长度为(n)。对于所有正整数(xin[1,k])求如下表达式$$sumlimits_{1leq l < rleq n}(a_l + a_r) ^ x$$

    数据范围:

    (1 leq n leq 2*10^5)

    (1 leq k leq 300)

    (1 leq a_i leq 10^8)

    思路:不难观察到(n)的范围非常自然的提高到了(2*10^5),但是(k)的范围特别小,(O(nk))是可以承受的复杂度,先可以提取到这个大概的复杂度的信息。其次可以通过式子想到二项式定理进行拆分,对于某个指定的(x),原式等价$$sumlimits_{1leq l < r leq n}sumlimits_{i=0}^x inom{x}{i}a_l^i a_r^{x-i}$$然而遗憾的是这个式子的形式还是非常复杂,由于(1 leq l < r leq n)这个非常丑的式子的存在,导致求和这件事情很难快速得到,但是想当然地,如果这里的限制条件是不存在的,范围是(1 leq l,r leq n)那么求和的表达式可以通过调换求和的顺序变成$$sumlimits_{i=0}xinom{x}{i}sumlimits_{l=1}n a_lisumlimits_{r=1}n a_r^{x-i}$$而式子的后半节是可以通过预处理在(O(nk))的代价下得到的,而系数部分可以通过乘法逆元以(O(N))的代价预处理。两边查询的代价都是(O(1))的。那么只要事情能走到这样对所有元素的求和,就可以化简到一个可以接受的式子。考虑如何拼出整个的求和:如果把原来式子的(l,r)调换得到的式子与原来的式子想加,那么得到的式子就是$$sumlimits_{1leq l,rleq n,l eq r}sumlimits_{i=0}xinom{x}{i}a_lia_r{x-i}$$那么,只需要把整个的部分里挖掉$l=r$的情况,也就是$$sumlimits_{j=1}nsumlimits_{i=0}xa_jia_j{x-i}inom{x}{i}$$接着可以发现后半部分很好合并,调换求和顺序之后得到$sumlimits_{j=1}^na_j^xsumlimits_{i=0}^ninom{x}{i}$根据二项式求和,可以进一步的得到$sumlimits_{j=1}^n(2a_j)^x$。于是可得,两个式子加起来的结果就是$$sumlimits_{i=0}xinom{x}{i}sumlimits_{l=1}na_lisumlimits_{r=1}n{a_r{x-i}}-sumlimits_{j=1}n(2a_j)x$$。回到之前的一步,将(l,r)调换,由于(inom{n}{i} == inom{x}{x-i})于是可以得到两个式子是相等的,即答案=上面的式子的一半。当然,这里题目要求取模,于是要乘乘法逆元。最后分别预处理,最后以(O(k^2))的代价计算所有结果即可。

    代码:

    #define _CRT_SECURE_NO_WARNINGS
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 2e5+7,M = 305,MOD = 998244353;
    ll fx[M],fc[M],fsa[M],fsb[M];
    int a[N],b[N];
    ll fa[N][M],fb[N][M];
    ll qmul(ll a, ll b, ll P) 
    {
      	ll L = a * (b >> 25LL) % P * (1LL << 25) % P;
      	ll R = a * (b & ((1LL << 25) - 1)) % P;
      	return (L + R) % P;
    }
    ll qpow(ll a,ll b,ll p)
    {
    	ll res = 1 % p;
    	while(b)
    	{
    		if(b & 1)	res = res * a % p;
    		a = a * a % p;
    		b >>= 1;
    	}
    	return res;
    }
    ll fact[N],infact[N];
    void init()
    {
        fact[0] = infact[0] = 1;
        for (int i = 1; i < N; i ++ )
        {
            fact[i] = ((ll)fact[i - 1] * i) % MOD;
            infact[i] = ((ll)infact[i - 1] * qpow(i, MOD - 2, MOD)) % MOD;
        }
    }
    ll C(int a,int b)
    {
        return (ll)fact[a]*infact[b]%MOD*infact[a-b]%MOD;
    }
    int main()
    {
    	init();
    	int n,k,rev2 = qpow(2,MOD - 2,MOD);scanf("%d%d",&n,&k);
        forn(i,1,n)	scanf("%d",&a[i]),b[i] = 2 * a[i];
    	fsa[0] = n,fsb[0] = n;
    	forn(i,1,n)
    	{
    		ll _a = 1,_b = 1;	
    		forn(j,1,k)
    		{
    			_a = _a * a[i] % MOD,_b = _b * b[i] % MOD;
    			fsa[j] = (fsa[j] + _a) % MOD,fsb[j] = (fsb[j] + _b) % MOD;
    		}
    	}
        
        forn(x,1,k)
        {
    		ll lf = 0;
    		forn(i,0,x)
    		{
    			ll t = fsa[i] * fsa[x - i] % MOD;
    			t = t * C(x,i) % MOD;
    			lf = (lf + t) % MOD;
    		}
    		// cout << lf << endl;
    		lf = ((lf - fsb[x]) % MOD + MOD) % MOD;
    		printf("%lld
    ",rev2 * lf % MOD);
        }
        return 0;
    }
    
    
  • 相关阅读:
    VC编译器
    位域
    位域2
    函数调用时刻的堆栈情况
    字节对齐2
    Python学习笔记(6)while循环、while嵌套
    Python学习笔记(7)字符串Str
    Python学习笔记(3)输出、输入、输出输入综合小练习
    Python学习笔记(1)Python介绍、解释器、第一个python程序、注释
    Python学习笔记(4)运算符、运算符小练习
  • 原文地址:https://www.cnblogs.com/HotPants/p/13875052.html
Copyright © 2020-2023  润新知