TJOI 2018 教科书般的亵渎 题解

题目传送门

题目大意: 场上有除了某 m m m 种以外血量为 1 1 1 ~ n n n 的随从,设 k k k 为杀死他们需要的亵渎数量,每张亵渎产生的贡献是:假如一只血量为 x x x 的随从被伤害了,那么提供贡献 x k x^k xk(一张亵渎多次伤害只计算最开始的那次),求贡献和。

题解

k k k 是容易求出来的,不难发现,每张亵渎收获的贡献和就是个自然数幂和,再减去其中不存在的随从的贡献即可,时间复杂度 O ( m 2 ) O(m^2) O(m2)

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define ll long long
#define mod 1000000007

int T,m,ans;ll n;
vector<ll> a;
int ksm(int x,int y){
    
    int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
void add(int &x,int y){
    
    x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){
    
    x=(x-y<0?x-y+mod:x-y);}
struct Lagrange{
    
    
	int fac[60],inv_fac[60];
	void init(){
    
    
		fac[0]=inv_fac[0]=1;
		for(int i=1;i<=55;i++)fac[i]=1ll*fac[i-1]*i%mod;
		inv_fac[55]=ksm(fac[55],mod-2);
		for(int i=54;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
	}
	int pre[60],suf[60];
	int calc(ll x,int k){
    
    
		if(x<=k+2){
    
    
			ll re=0;
			for(int i=1;i<=x;i++)re+=ksm(i,k);
			return re%mod;
		}else{
    
    
			pre[0]=1;for(int i=1;i<=k+2;i++)pre[i]=1ll*(x-i)%mod*pre[i-1]%mod;
			suf[k+3]=1;for(int i=k+2;i>=1;i--)suf[i]=1ll*(x-i)%mod*suf[i+1]%mod;
			int re=0,sum=0;
			for(int i=1;i<=k+2;i++){
    
    
				sum=(sum+ksm(i,k))%mod;
				(k+2-i&1?dec:add)(re,1ll*sum*pre[i-1]%mod*suf[i+1]%mod*inv_fac[i-1]%mod*inv_fac[k+2-i]%mod);
			}
			return re;
		}
	}
}Lag;

int main()
{
    
    
	Lag.init();
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%lld %d",&n,&m);
		a.clear();
		for(int i=1;i<=m;i++){
    
    
			ll x;
			scanf("%lld",&x);
			a.push_back(x);
		}
		a.push_back(0);a.push_back(n+1);
		sort(a.begin(),a.end());
		int k=a.size()-1;ll now=n+1;
		while(k>=0&&a[k]==now)k--,now--;k++;
		
		ans=0;
		for(int i=0;i<a.size()-1;i++){
    
    
			add(ans,Lag.calc(n-a[i],k));
			for(int j=i+1;j<a.size()-1;j++)
				dec(ans,ksm(a[j]-a[i],k));
		}
		printf("%d\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/112612509